普通函数
this 绑定原则
- this 的绑定和定义的位置(编写的位置)没有关系
- this 的绑定和调用方式以及调用的位置有关系
- this 是在运行时被绑定的
this 绑定规则
默认绑定
默认绑定 即 独立函数调用(函数没有被绑定到某个对象上进行调用)
隐式绑定
隐式绑定 就是通过某个对象发起的函数调用
隐式绑定的前提条件
- 必须在调用的对象内部有一个对函数的引用
- 如果没有这样的引用,在进行调用时,会找不到该函数报错
- 通过这个引用,间接的将 this 绑定到了这个对象上
显示绑定
我们不希望在对象的内部包含这个函数的引用,同时又希望在这个对象上强制调用,该怎么做呢?
-
call
function foo(){
console.log('测试 call');
}
foo.call({name:'obj'}); -
apply
function foo(){
console.log('测试 call');
}
foo.apply({name:'obj'}); -
bind
function foo(){
console.log('测试 call');
}
var b = foo.bind({name:'obj'});
b();
new 绑定
new 绑定 的工作如下:
- 创建一个全新的对象
- 这个新对象会进行 prototype 连接
- 这个新对象会绑定到函数调用的 this 上
- 如果函数没有返回其他对象,表达式会返回这个新对象
this 绑定规则优先级
- 默认规则的优先级最低
- 显示绑定优先级高于隐式绑定
- new绑定优先级高于隐式绑定
- new绑定优先级高于bind
- new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
- new绑定可以和bind一起使用,new绑定优先级更高
内置函数中的 this 绑定
DOM 事件中的 this 绑定
-
通过
onclick
绑定事件: 事件函数通过box
隐式绑定,指向box
<div class="box"></div>
<script>
const box = document.querySelector('.box');
box.onclick = function(e){
console.log(e);
console.log(this); // 指向 box 元素
}
</script> -
通过
addEventListener
绑定事件: 事件函数通过box
隐式绑定,指向box
<div class="box"></div>
<script>
const box = document.querySelector('.box');
box.addEventListener('click',function(e){
console.log(e);
console.log(this); // 指向 box 元素
});
box.addEventListener('click',function(e){
console.log(e);
console.log(this); // 指向 box 元素
});
box.addEventListener('click',function(e){
console.log(e);
console.log(this); // 指向 box 元素
});
setTimeout 中的 this 绑定
-
普通函数: 该函数独立调用,this 绑定到了全局执行上下文 window
setTimeout(function(){
console.log(this); // 该函数独立调用,this 绑定到了全局执行上下文 window
},200); -
箭头函数: 该函数创建环境所在的上层作用域为全局,所以 this 指向为 window
setTimeout(() => {
console.log(this); // Window {window: Window, self: Window, document: document, name: '', location: Location, …}
}, 200);
JS 数组方法中的 this 绑定
-
普通函数:
array.forEach()
内部callback()
, 回调函数独立调用,this 指向 windowconst array = ['a','b','c'];
array.forEach(function(value,index,arr){
console.log(value,index,arr); // a 0 (3) ['a', 'b', 'c'] 循环遍历 arr 指向数组
console.log(this); // this 指向 window
}); -
箭头函数:
array.forEach()
内部callback.call(thisArg)
, 通过 call 显示绑定了 array, this 指向 arrayconst array = ['a','b','c'];
array.forEach(function(value,index,arr){
console.log(value,index,arr); // a 0 (3) ['a', 'b', 'c'] 循环遍历 arr 指向数组
console.log(this); // this 指向 array
},array);
this 面试常考问题
-
根据问题,输出结果--forEach 改变 this 指向
function foo (value){
console.log(value,this.name);
}
const obj = {
name:'柏拉文'
};
[1,2,3].forEach(foo,obj);
// 结果为:
// 1 '柏拉文'
// 2 '柏拉文'
// 3 '柏拉文' -
根据问题,输出结果,并改正--对象属性为函数,且函数里面嵌套函数
-
this 指向 window
const obj = {
data:[],
getData:function(){
setTimeout(function(){
this.data = [1,2,3]; // setTimeout 中的 callback 回调函数是 window 调用的,所以回调函数中的 this 指向 window , this.data === window.data
console.log(this,this.data); // window window.data = [1,2,3]
},100);
}
}
obj.getData(); -
更改 this 指向方案一: 将 this 赋值给另一个变量保存指向
const obj = {
data:[],
getData:function(){ // getData 函数被 obj 对象调用,所以 getData 函数中的 this 指向 obj
const that = this; // 将 this 赋值给 that , that 指向 obj
setTimeout(function(){
that.data = [1,2,3];
console.log(that,that.data);
},100);
}
}
obj.getData(); -
更改 this 指向方案二 将自身调用的函数通过 bind 绑定指向
const obj = {
data:[],
getData:function(){
setTimeout(function(){
this.data = [1,2,3];
console.log(this,this.data);
}.bind(this),100);
}
}
obj.getData(); -
更改 this 指向方案三 将自身调用的函数使用箭头函数
const obj = {
data:[],
getData:function(){
setTimeout(()=>{
this.data = [1,2,3];
console.log(this,this.data);
},100);
}
}
obj.getData();
-
-
根据问题,说出输出结果--对象函数相互嵌套之对象属性
var name = "windowName";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
},
};
function sayName() {
var sss = person.sayName;
sss(); // 自身独立函数调用,this 指向为 window ====> 结果为 windowName
person.sayName(); // person 隐式调用, this 指向为 person ====> 结果为 person
(person.sayName)(); // person 隐式调用, this 指向为 person ====> 结果为 person
(b = person.sayName)(); // 自身独立函数调用, this 指向为 window ====> 结果为 windowName
}
sayName(); -
根据问题,说出输出结果--对象函数相互嵌套之 call 与 this
var name = "windowName";
var person1 = {
name:'person1Name',
foo1:function(){
console.log(this.name);
},
foo2:()=>{
console.log(this.name);
},
foo3:function(){
return function(){
console.log(this.name);
}
},
foo4:function(){
return ()=>{
console.log(this.name);
}
}
}
var person2 = {name:"person2Name"};
person1.foo1(); // person1 隐式绑定,this 指向为 person1 ====> 结果为 person1Name
person1.foo1.call(person2); // person1 隐式绑定 、 person2 显示绑定 ,且显示绑定优先级高于隐式绑定,this 指向为 person2 ====> 结果为 person2Name
person1.foo2(); // person1 调用 foo2 ,但是 foo2 为箭头函数,不绑定 this ,所以 foo2 中的 this 指向根据外层作用域决定, foo2 的上层作用域为 window (注意对象不是作用域,作用域只包括全局、函数、块级),所以 this 指向为 window ====> 结果为 windowName
person1.foo2.call(person2); // person1 调用 foo2 ,且 通过 call 指向了 person 2 。但是 foo2 为箭头函数,不绑定 this ,所以 foo2 中的 this 指向根据上层作用域决定, foo2 的上层作用域为 window (注意对象不是作用域,作用域只包括全局、函数、块级),所以 this 指向为 window ====> 结果为 windowName
person1.foo3()(); // person1.foo3() 返回一个函数,并且自身独立调用 ===> 结果为 windowName
person1.foo3.call(person2)(); // person1.foo3.call(person2) 相当于 person1.foo3() 且里面的 this 指向为 person2 ,但是 person1.foo3()() 还是自身独立调用 ===> 结果为 windowName
person1.foo3().call(person2); // person1.foo3() 返回一个函数,这个返回的函数通过 call 将自身的 this 指向了 person2 ===> 结果为 person2Name
person1.foo4()(); // person1.foo4() 返回一个箭头函数,并且自身独立调用但是这个箭头函数的上层作用域为 person.foo4() ,所以该箭头函数调用时,this 指向的是上层作用域 person.foo4() 里面的 this,而 person.foo4() 里面的 this 指向 person1 ===> 结果为 person1Name
person1.foo4.call(person2)(); // person1.foo4.call(person2) 相当于 person1.foo4() 且里面的 this 指向为 person2, person1.foo4.call(person2) 返回一个箭头函数,箭头函数独立调用,但是箭头函数不绑定 this,由上层作用域决定,箭头函数的上层作用域为 person1.foo4(),此时 person1.foo4() 中的 this 通过 call 指向了 person2 ===> 结果为 person2Name
person1.foo4().call(person2); // person1.foo4() 返回了一个箭头函数,并且通过 call 改变this但是箭头函数不绑定 this ,所以该箭头函数调用时,this 指向的是上层作用域 person.foo4() 里面的 this,而 person.foo4() 里面的 this 指向 person1 ===> 结果为 person1Name -
根据问题,说出输出结果--对象函数相互嵌套之 new、call 与 this
var name = 'windowName';
function Person(name){
this.name = name;
this.foo1 = function(){
console.log(this.name);
}
this.foo2 = ()=> console.log(this.name);
this.foo3 = function(){
return function(){
console.log(this.name);
}
}
this.foo4 = function(){
return ()=>{
console.log(this.name);
}
}
}
var person1 = new Person('person1Name');
var person2 = new Person('person2Name');
person1.foo1();// person1 调用 foo1() ,隐式绑定 this,this 指向 person1 ===> 结果为 person1Name
person1.foo1.call(person2);//person1 调用 foo1(), 通过 call 显示绑定 this 到 person2 ,this 指向 person2 ===> 结果为 person2Name
person1.foo2();//person1 调用 foo2() , 隐式绑定 this 。但是 foo2 为箭头函数,箭头函数不绑定 this ,里面的 this 由上层作用域决定,foo2 箭头函数的上层作用域为 Person ,而 foo2 是通过 person1 调用的,所以 此时 Person 的 this 指向 person1 ==> 结果为 person1Name
person1.foo2.call(person2);//person1 调用 foo2(), 通过 call 显示绑定 this。但是 foo2 为箭头函数,箭头函数不绑定 this ,里面的 this 由上层作用域决定,foo2 箭头函数的上层作用域为 Person, 而 foo2 是通过 person1 调用的,所以此时 Person 的 this 指向person1 ==> 结果为 person1Name
person1.foo3()();// person1.foo3() 返回一个普通函数,普通函数自身独立调用 , this 指向 window ===> 结果为 windowName
person1.foo3.call(person2)();// person1.foo3 函数通过 call 改变了 person1.foo3 中的 this 指向,返回了一个普通函数,普通函数自身独立调用 , this 指向 window ===> 结果为 windowName
person1.foo3().call(person2);// perso1.foo3 函数返回了一个普通函数,这个普通函数通过 call 显示绑定自身 this 指向为 person2 ===> 结果为 person2Name
person1.foo4()();// person1.foo4() 函数返回了一个箭头函数,箭头函数自身独立调用。但是箭头函数自身不绑定 this, 箭头函数的 this 要看上层作用域,该箭头函数的上层作用域为 person1.foo4() ,person1.foo4() 中的 this 指向 person1,所以该箭头函数 this 也指向 person1 ===> 结果为 person1Name
person1.foo4.call(person2)();// person1.foo4 函数通过 call 显示绑定自身 this 指向为 person2,并且返回一个箭头函数,箭头函数自身独立调用。但是箭头函数自身不绑定 this, 箭头函数的 this 要看上层作用域,该箭头函数的上层作用域为 person1.foo4() ,person1.foo4() 中的 this 通过 call 显示绑定了 person2,所以该箭头函数 this 也指向 person2 ===> 结果为 person2Name
person1.foo4().call(person2);// person。foo4 函数返回一个箭头函数,箭头函数通过 call 显示绑定 this 指向为 person2。但是箭头函数自身不绑定 this, 箭头函数的 this 要看上层作用域,该箭头函数的上层作用域为 person1.foo4() ,person1.foo4() 中的 this 指向为 person1,所以该箭头函数 this 也指向 person1 ===> 结果为 person1Name -
根据问题,说出输出结果--对象函数相互嵌套之 new、call 与 this 加强版
var name = "window";
function Person(name){
this.name = name;
this.obj = {
name:'obj',
foo1:function(){
return function (){
console.log(this.name);
}
},
foo2:function(){
return ()=>{
console.log(this.name);
}
}
}
}
var person1 = new Person('person1');
var person2 = new Person('person2');
person1.obj.foo1()();// person1.obj 调用 foo1 函数,返回一个普通函数。普通函数自身独立调用,this 指向 window ===> 结果为 window
person1.obj.foo1.call(person2)();// person1.obj 通过 call 显示绑定 foo1 函数中的 this 指向为 person2,返回一个普通函数。普通函数自身独立调用,this 指向 window ===> 结果为 window
person1.obj.foo1().call(person2);// person1.obj 调用 foo1 函数,返回一个普通函数。普通函数通过 call 显示绑定 自身this 指向为 person2 ===> 结果为 person2
person1.obj.foo2()();// person1.obj 调用 foo2,返回一个箭头函数。箭头函数自身独立调用,但是箭头函数不绑定 this ,自身this 跟随上层作用域。该箭头函数的上层作用域为 obj.foo2(),obj.foo2() 的 this 指向为 obj,所以该箭头函数的 this 指向为 obj ==> 结果为 obj
person1.obj.foo2.call(person2)();// person.obj 通过 call 显示绑定 foo2 函数中 this 指向为 person2,返回一个箭头函数。箭头函数自身独立调用,但是箭头函数不绑定 this ,自身this 跟随上层作用域。该箭头函数的上层作用域为 obj.foo2(),obj.foo2() 的 this 通过 call 显示绑定为 person2,所以该箭头函数的 this 指向为 person2 ==> 结果为 person2
person1.obj.foo2().call(person2);// person.obj 调用 foo2 函数,返回一个箭头函数。箭头函数通过 call 显示绑定 this 为 person2,但是箭头函数不绑定 this ,自身this 跟随上层作用域。该箭头函数的上层作用域为 obj.foo2(),obj.foo2() 的 this 指向为 obj,所以该箭头函数的 this 指向为 obj ==> 结果为 obj