this的绑定规则有:
- 默认绑定
- 隐式绑定
- 显式绑定
- new绑定
用一句话总结this的指向规则:
this的指向,是在执行函数时根据执行上下文所动态决定的
(调用函数会创建新的属于函数自身的上下文,执行上下文的调用创建阶段会决定this的指向)
一般来说,this 绑定的优先级: new > 显示绑定 > 隐式绑定 > 默认绑定。
1. 默认绑定
var a = 'global'
function fn1() {
console.log(this)
}
fn1(); //global
函数在全局环境调用执行时,this指向全局对象,如果使用严格模式,this绑定到undefined
2. 隐式绑定
var a = 'globalA';
var obj = {
a: 'objA',
test
}
function test () {
console.log(this.a)
}
obj.test(); // objA
函数作为对象的方法被调用时,this指向方法运行时所在的当前对象,即obj。
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
function test() {
console.log(this.a);
}
obj1.obj2.foo(); // 42
如果是多重对象的调用,this 也还是绑定在上一级对象上,即obj2上。
对象属性引用链中只有最后一层会影响调用位置。
2.1.1 隐式丢失
var a = 'global'
const zhangsan = {
name: "张三",
sayHi () {
console.log(this);
},
wait() {
function foo1() {
console.log(this.a);
}
foo1();
}
};
zhangsan.wait(); //global
这里的函数调用比上面的程序多了一层函数的嵌套,绑定的 this 就发生了变化,变成了 winodw 全局对象
解决办法:
wait() {
var that = this
function foo1() {
console.log(that.a);
}
foo1();
}
或者使用箭头函数
wait() {
const foo1 = () => {
console.log(this.a);
}
foo1();
}
箭头函数内的this取上级作用域(waitAgain方法)的this,即waitAgain方法运行时所在的当前对象张三。
箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定。this指向箭头函数上级作用域指向的this,this是继承自父执行上下文!!!
箭头函数面试题:
var name = 'window';
const person1 = {
name: 'person1',
sayName: () => {
console.log(this.name);
}
}
person1.sayName();//window
var b = '0'
let obj = {
b: 1,
a () {
var a = '2'
return () => {
return () => {
console.log(this.b) // 1
}
}
}
}
obj.a()()()
// this指向的是obj
// {a: ƒ}
var name = 'a'
let obj = {
a () {
console.log(this)
},
name: 'jack',
showName: this.name // 箭头函数
}
obj.a() // obj
console.log(obj.showName) // a
let a = obj.a
a() // window
3. 显示绑定
使用call,apply,bind改变函数的调用对象,第一个参数就是改变后的调用这个函数的对象
function fn1() {
console.log(this)
}
fn1.call({x:100}); //{x: 100}
使用call,apply,bind方法调用时,第一个参数就是this指向的对象{x:100}
call,apply,bind的区别
call、apply、bind的作用是改变函数运行时this的指向,所以它们的第一个参数是要绑定给this的值,区别在于:
fn.call({x:100},p1,p2,p3) //call后面的参数是一个参数列表,一个一个传进去的
fn.apply({x:100},argument) //apply里面的第二个参数是一个参数数组
fn.bind({x:100},p1,p2,p3) //bind和call相似,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数,没有立即执行。
4. new绑定
var x = "global!";
function obj(){
this.x = "obj!";
}
var o = new obj();
console.log(o.x); //obj!
函数通过new调用后,会返回一个新对象,并将新对象会绑定到函数调用的 this上
使用 new 调用函数,会自动执行下面的操作:
1.创建一个新对象。
2. 将新对象绑定到函数调用的 this上。这个新对象会被执行 [[prototype]] 连接。对象.proto = 构造函数.prototype
3. 执行构造函数
4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
面试题
构造函数中返回一个对象对结果有什么影响???
function Super (a) {
this.a = a;
return 123;
}
Super.prototype.sayHello = function() {
alert('hello world');
}
function Super_ (a) {
this.a = a;
return {a: 2};
}
Super_.prototype.sayHello = function() {
alert('hello world');
}
const super1 = new Super(3)
const super2 = new Super_(3)
console.log(super1) // Super { a: 3 }
console.log(super2) // { a: 2 }
在构造函数中 return 基本类型 不会影响构造函数的值, 而 return 对象类型 则会替代构造函数返回该对象。