apply和call方法的使用
//apply和call的使用
//作用:可以改变this的指向
//apply和call方法中如果没有传入参数,或者是传入的是null,那么调用该方法的函数对象中的this就是默认的window
*JavaScript中存在继承,通过调用apply或call传过去的第一个参数来动态指定方法中的 this .
function f1(x,y) {
console.log("结果是:"+(x+y)+this);
return "10000";
}
f1.apply();
f1.call();
f1.apply(null);
f1.call(null);
apply和call都可以让函数或者方法来调用,传入参数和函数自己调用的写法不一样,但是效果是一样的
f1.apply(null,[100,200]);
f1.call(null,100,200);
apply和call的使用方法
/*
* apply的使用语法
* 函数名字.apply(对象,[参数1,参数2,...]);
* 方法名字.apply(对象,[参数1,参数2,...]);
* call的使用语法
* 函数名字.call(对象,参数1,参数2,...);
* 方法名字.call(对象,参数1,参数2,...);
*
* 作用:改变this的指向
* 不同的地方:参数传递的方式是不一样的
* 只要是想使用别的对象的方法,并且希望这个方法是当前对象的,那么就可以使用apply或者是call的方法改变this的指向
function f1() {
console.log(this+":====>调用了");
}
f1是函数,f1也是对象,因为f1中有 __proto__和prototype
console.dir(f1);
对象调用方法,说明,该对象中有这个方法
f1.apply();
f1.call();
console.log(f1.__proto__==Function.prototype);
所有的函数都是Function的实例对象
console.log(Function.prototype);//ƒ () { [native code] }
console.dir(Function);
apply和call方法实际上并不在函数这个实例对象中,而是在Function的prototype中
bind是用来复制一份 //使用的语法: /*理解:将改变this后的函数状态复制了一份。函数调用之后内部this使用的是指定后的 * 函数名字.bind(对象,参数1,参数2,...);---->返回值是复制之后的这个函数 * 方法名字.bind(对象,参数1,参数2,...);---->返回值是复制之后的这个方法
function Person(age) {
this.age=age;
}
Person.prototype.play=function () {
console.log(this+"====>"+this.age);
};
function Student(age) {
this.age=age;
}
var per=new Person(10);
var stu=new Student(20);
//复制了一份
var ff=per.play.bind(stu);
ff(); [object Object]====>20
bind方法的使用
<script>
通过对象,调用方法,产生随机数
function ShowRandom() {
1-10的随机数
this.number=parseInt(Math.random()*10+1);
}
添加原型方法
ShowRandom.prototype.show1=function () {
改变了定时器中的this的指向了,本来应该是window,现在是实例对象了
window.setInterval(this.show2.bind(this),1000);
};
添加原型方法
ShowRandom.prototype.show2=function () {
显示随机数--
console.log(this.number);
};
实例对象
var sr=new ShowRandom();
调用方法,输出随机数字
调用这个方法一次,可以不停的产生随机数字
sr.show1();
</script>
函数中的几个成员
//函数中有一个name属性----->函数的名字,name属性是只读的,不能修改
//函数中有一个arguments属性--->实参的个数
//函数中有一个length属性---->函数定义的时候形参的个数
//函数中有一个caller属性---->调用(f1函数在f2函数中调用的,所以,此时调用者就是f2)
function f1(x,y) {
console.log(f1.name);
console.log(f1.arguments.length);
console.log(f1.length);
console.log(f1.caller);//调用者
}
函数还可以通过参数传递来实现调用,在函数体中执行函数
function f1(fn) {
setInterval(function () {
console.log("定时器开始");
fn();
console.log("定时器结束");
},1000);
}
f1(function () {
console.log("好困啊,好累啊,就是想睡觉");
});
函数作为返回值
可以参考java中设置toString方法进行理解
var num=10;
console.log(typeof num); number
var obj={};
console.log(obj instanceof Object); true
更改this为obj,直接打印obj的使用会显示其数据类型
Object.prototype.toString.call(obj);
console.log(obj) {}
获取数据类型,修改this指向具体类型数据,通过toString方法进行打印
console.log(Object.prototype.toString.call([])); [object Array]
console.log(Object.prototype.toString.call(new Date())); [object Date]
函数作为返回值的使用
function getFunc(type) {
return function (obj) {
return Object.prototype.toString.call(obj) === type;
}
}
var ff = getFunc("[object Array]");
var result = ff([10, 20, 30]);
console.log(result); true
函数作为参数练习,有点像java中的重写equals方法
*sort方法会从零号位置进行遍历和比较,如果返回值大于零,则进行交换。
var arr = [1, 100, 20, 200, 40, 50, 120, 10];
//排序---函数作为参数使用,匿名函数作为sort方法的参数使用,那么此时的匿名函数中有两个参数,
arr.sort(function (obj1,obj2) {
if(obj1>obj2){
return -1;
}else if(obj1==obj2){
return 0;
}else{
return 1;
}
});
console.log(arr);
var arr1=["acdef","abcd","bcedf","bced"];
arr1.sort(function (a,b) {
if(a>b){
return 1;
}else if(a==b){
return 0;
}else{
return -1;
}
});
console.log(arr1);
案例:函数作为参数
function File(name, size, time) {
this.name = name;//电影名字
this.size = size;//电影大小
this.time = time;//电影的上映时间
}
var f1 = new File("jack.avi", "400M", "1997-12-12");
var f2 = new File("tom.avi", "200M", "2017-12-12");
var f3 = new File("xiaosu.avi", "800M", "2010-12-12");
var arr = [f1, f2, f3];
function fn(attr) {
//函数作为返回值
return function getSort(obj1, obj2) {
if (obj1[attr] > obj2[attr]) {
return 1;
} else if (obj1[attr] == obj2[attr]) {
return 0;
} else {
return -1;
}
}
}
var ff = fn("name");
//函数作为参数
arr.sort(ff);
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
}
作用域和作用域链和预解析
//变量---->局部变量和全局变量,
//作用域:就是变量的使用范围
//局部作用域和全局作用域
//js中没有块级作用域---一对括号中定义的变量,这个变量可以在大括号外面使用
//函数中定义的变量是局部变量
if(true){
var num=10;
}
console.log(num) 10
function fn() {
var num2=20;
}
console.log(num2); 报异常 num2 is not defined
作用域链:变量的使用,从里向外,层层的搜索,搜索到了就可以直接使用了层层搜索,搜索到0级作用域的时候,如果还是没有找到这个变量,结果就是报错
*如果里层没有对应的变量,它会一层一层想外查找
var num=10;
function f() {
var num=20;
function f1() {
var num=30;
console.log(num);
}
f1();
}
f();
//预解析:就是在浏览器解析代码之前,把变量的 声明 和 函数的声明 提前(提升)到该作用域的最上面
console.log(num);
var num=10; undefined 声明提前了,但是没有初始化
f(); 函数执行了,预解析函数提前了
function f() {
console.log('函数执行了')
}
console.log(f1); undefined 函数的声明提前了
var f1=function() {
console.log('声明提前了')
}
闭包
* 闭包的概念:函数A中,有一个函数B,函数B中可以访问函数A中定义的变量或者是数据,此时形成了闭包(这句话暂时不严谨)
* 闭包的模式:函数模式的闭包,对象模式的闭包
* 闭包的作用:缓存数据,延长作用域链
* 闭包的优点和缺点:缓存数据
函数模式的闭包:在一个函数中有一个函数
function f1() {
var num=10;
//函数的声明
function f2() {
console.log(num); 10
}
//函数调用
f2();
}
f1();
对象模式的闭包:函数中有一个对象
function f3() {
var num=10;
var obj={
age:num
};
console.log(obj.age);//10
}
f3();
闭包小练习
function f() {
var num=10;
return function(){
num++;
return num;
}
}
var fn=f();
console.log(fn()); 11
console.log(fn()); 12
console.log(fn()); 13
总结:如果想要缓存数据,就把这个数据放在外层的函数和里层的函数的中间位置
//闭包的作用:缓存数据.优点也是缺陷,没有及时的释放
//局部变量是在函数中,函数使用结束后,局部变量就会被自动的释放
//闭包后,里面的局部变量的使用作用域链就会被延长
function showRandom() {
var num=parseInt(Math.random()*10+1);
console.log(num);
}
showRandom();
showRandom();
showRandom();
实现了缓存功能的闭包,多次调用打印的数字都一样
function f1() {
var num=parseInt(Math.random()*10+1);
return function () {
console.log(num);
}
}
var ff=f1();
ff();
ff();
ff();
案例:点赞小案例
<style>
ul {
list-style-type: none;
}
li {
float: left;
margin-left: 10px;
}
img {
width: 200px;
height: 180px;
}
input {
margin-left: 30%;
}
</style>
<script>
//$永远都是24k纯帅的十八岁的杨哥$
</script>
</head>
<body>
<ul>
<li><img src="images/ly.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
...若干个...
</ul>
<script>
//获取所有的按钮
//根据标签名字获取元素
function my$(tagName) {
return document.getElementsByTagName(tagName);
}
//闭包缓存数据
function getValue() {
var value=2;
return function () {
//每一次点击的时候,都应该改变当前点击按钮的value值
this.value="赞("+(value++)+")";
}
}
//获取所有的按钮
var btnObjs=my$("input");
//循环遍历每个按钮,注册点击事件
for(var i=0;i<btnObjs.length;i++){
//注册事件
btnObjs[i].onclick=getValue();
}
沙箱:环境,黑盒,在一个虚拟的环境中模拟真实世界,做实验,实验结果和真实世界的结果是一样,但是不会影响真实世界
var num=10;
(function () {
var num=20;
console.log(num+10); 30
})();
沙箱小案例
(function () {
document.getElementById('btn').onclick=function () {
console.log('按钮被点击了')
}
})();
(function () {
var str='我是中国人';
console.log(str.substr(2)) 中国人
})();
沙箱小案例
<div>这是div</div>
<div>这是div</div>
<p>这是p</p>
<p>这是p</p>
<script>
var getTag = 10;
var dvObjs = 20;
var pObjs = 30;
(function () {
//根据标签名字获取元素
function getTag(tagName) {
return document.getElementsByTagName(tagName)
}
//获取所有的div
var dvObjs = getTag("div");
for (var i = 0; i < dvObjs.length; i++) {
dvObjs[i].style.border = "2px solid pink";
}
//获取所有的p
var pObjs = getTag("p");
for (var i = 0; i < pObjs.length; i++) {
pObjs[i].style.border = "2px solid pink";
}
}());
console.log(getTag); 10
console.log(dvObjs); 20
console.log(pObjs); 20
递归:函数中调用函数自己,此时就是递归,递归一定要有结束的条件
function f(n) {
if(n==1){
return 1;
}
return n+f(n-1);
}
console.log(f(10));