this指向
① this并不指向函数,而是指向调用它的主体对象。
③ this指向的对象,我们称之为函数的上下文context,也叫函数的调用者
- 普通函数调用:通过函数名()直接调用:this指向全局对象window(注意let定义的变量不是window属性,只有window.xxx定义的才是。即let a =’aaa’; this.a是undefined)
- 构造函数调用:函数作为构造函数,用new关键字调用时:this指向新new出的对象
- 对象函数调用:通过对象.函数名()调用的:this指向这个对象
- 箭头函数调用:箭头函数里面没有 this ,所以永远是上层作用域this(上下文)
- apply和call调用:call/apply方法第一个参数是函数体内 this 的指向
- 函数作为数组的一个元素,通过数组下标调用的:this指向这个数组
- 函数作为window内置函数的回调函数调用:this指向window(如setInterval setTimeout 等)
严格模式下, this为undefined
console.log("通过函数名()直接调用:this指向window");
let x = 2;
function test() {
console.log(this);
console.log(this.x);
console.log(x);
}
test();//Object [global]
console.log("通过对象.函数名()调用的:this指向这个对象");
let obj = {
x : 3,
objTestest: test,
};
obj.objTestest(); //{ x: 3, objTestest: [Function: test] }
console.log("函数作为构造函数,用new关键字调用时:this指向新new出的对象");
let newobj = new test(); //test {}
console.log("函数作为数组的一个元素,通过数组下标调用的:this指向这个数组");
let arr = [1,2,test,4]; //test表示的是函数对象,即整个这个function,而test()表示调用函数,有return就对应一个返回值
arr[2](); //[ 1, 2, [Function: test], 4 ]
console.log("函数作为window内置函数的回调函数调用:this指向window(如setInterval setTimeout等)");
setTimeout(test,0); //Timeout {}
console.log("普通函数与箭头函数");
let obj={
a:222,
fn:function(){
setTimeout(function(){console.log(this.a)})
}
};
obj.fn();//undefined
//this 指向是 window setTimeout,window 下面没有a ,所以这里输出 undefined。
let obj={
a:222,
fn:function(){
setTimeout(()=>{console.log(this.a)});
}
};
obj.fn();//222
//这次输出 222 是因为,传给 setTimeout 的是箭头函数,然后箭头函数里面没有 this ,所以要向上层作用域查找,在这个例子上, setTimeout 的上层作用域是 fn。而 fn 里面的 this 指向 obj ,所以 setTimeout 里面的箭头函数的 this ,指向 obj 。所以输出 222
console.log("call/apply");
let obj1={
a:222
};
let obj2={
a:111,
fn:function(){
alert(this.a);
}
}
// obj2.fn.call(obj1); /call 和 apply 两个主要用途就是
// 1.改变this的指向 (把 this 从 obj2 指向到 obj1 )
// 2.方法借用 (obj1 没有 fn ,只是借用 obj2 方法)
输出结果:
通过函数名()直接调用:this指向window
Object [global] { ... }
undefined (这个不会输出上面定义的let对象,因为他不是window,换成window.x = 2就输出2了)
2
通过对象.函数名()调用的:this指向这个对象
{ x: 3, objTestest: [Function: test] }
3
2
函数作为构造函数,用new关键字调用时:this指向新new出的对象
test {}
undefined
2
函数作为数组的一个元素,通过数组下标调用的:this指向这个数组
[ 1, 2, [Function: test], 4 ]
undefined
2
函数作为window内置函数的回调函数调用:this指向window( setInterval setTimeout 等)
Timeout { ... }
undefined
2
注意全局环境下定义的变量不是全局属性哦。如果没有定义window.namef,会输出undefined
参考:https://blog.csdn.net/weixin_37722222/article/details/81625826 这个讲的很好
普通函数与箭头函数
区别:
1.写法
// es5
var fn = function(a, b) {
return a + b;
}
// es6 箭头函数写法,当函数直接被return时,可以省略函数体的括号
const fn = (a, b) => a + b;
// es5
var foo = function() {
var a = 20;
var b = 30;
return a + b;
}
// es6
const foo = () => {
const a = 20;
const b = 30;
return a + b;
}
当函数参数只有一个时,括号可以省略。函数体(中括号)中有且只有一行return语句时,中括号及return 关键字可以省略。
2.this指向
箭头函数中没有this。如果你在箭头函数中使用了this,那么该this一定就是外层的this。
function A() {
let a = 3;
this.a = 4;
console.log(this); //A { a: 4 }
setTimeout(function() {
console.log(a) //3
},100);
setTimeout(function() {
console.log(this.a);//undefined this指向是Timeout{}方法
},200);
setTimeout(() => {
console.log(a) //3
},300);
setTimeout(() => {
console.log(this.a) //4 在箭头函数里面,没有 this ,箭头函数里面的 this 是继承外面的环境。
},400)
}
new A();
3.构造函数
function是可以定义构造函数的,而箭头函数是不行的。
//使用function方法定义构造函数
function Person(name, age){
this.name = name;
this.age = age;
}
var lenhart = new Person(lenhart, 25);
console.log(lenhart); //{name: 'lenhart', age: 25}
//尝试使用箭头函数
var Person = (name, age) =>{
this.name = name;
this.age = age;
};
var lenhart = new Person('lenhart', 25); //Uncaught TypeError: Person is not a constructor
4.变量提升
由于js的内存机制,function提升的级别最高,而用箭头函数定义函数的时候,需要var/ let/const关键词,变量提升只会提升变量名的声明,而不会提升变量的赋值初始化,故箭头函数一定要定义于调用之前!
aaa();
bbb(); //报错ReferenceError: bbb is not defined
function aaa() {
console.log(1);
}
let bbb = ()=>console.log(2);
call/apply/bind
相同:
三者都是用来改变函数的this对象的指向的;
三者第一个参数都是this要指向的对象,也就是想指定的上下文;
三者都可以利用后续参数传参;
不同:
apply和call传入的参数列表形式不同。appl接收arguments,call接收一串参数列表
bind 主要就是将函数绑定到某个对象,bind()会创建一个函数,返回对应函数便于稍后调用;而apply、call则是立即调用。
var obj = {
x: 81,
};
var foo = {
getX: function() {
return this.x;
}
};
var getX = foo .getX;
getX (); // 9 因为"this"指向全局对象
console.log(foo.getX.bind(obj)()); //81(方法名后多了括号)
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81
arguments
arguments与数组
相同点:
- 都可用下标访问每个元素
- 都有length属性
不同点:
- 数组对象的类型是Array,类数组对象的类型是Object;
- 类数组对象不能直接调用数组API;
- 数组遍历可以用for in和for循环,类数组只能用for循环遍历;
arguments 用法
- 实现重载(overload):当函数的参数个数不明确时,函数体根据参数的不同进行相应处理arguments.length
- 实现递归:在函数内部反复的调用函数本身arguments.callee(在严格模式下不允许arguments.callee)
function factorial(num) {
if(num<=1) {
return 1;
}else {
return num * arguments.callee(num-1);
}
}
arguments 转数组
Array.prototype.slice.call(arguments);
或者 [ ].slice.call(arguments);
Array.from() 是个非常推荐的方法,其可以将所有类数组对象转换成数组。
arguments 参考: https://blog.csdn.net/xiaotao_css/article/details/72794650