深入剖析This机制

JavaScript中有许多令人困惑的地方,但是,因为这些东西让JavaScript更加强大。例如原型链,闭包等,其中包括this机制,处处坑。

This绑定规则

this机制有四种绑定的规则:默认绑定,隐式绑定 ,显式绑定 ,new绑定,分别对应函数四种调用方式:函数直接调用 , 对象方法调用,间接调用 ,构造函数调用。

**非严格模式下: ** this都会指向一个对象

默认绑定

全局环境,this指向window

console.log(this == window);//true

独立调用的方法,this指向window

function fn (){
    console.log(this==window);//true
}

调用嵌套的函数,this指向window

function fn (){
    function test(){
        console.log(this == window);//true
    }
    test();
}
fn();

以上是在非严格模式下情况。

在严格模式下,默认绑定有区别。非严格模式下this会绑定到上级作用域,而严格模式(use strict)时,不会绑定到window对象身上

function fn(){
    'use strict';
    console.log(this);//undefined
}
fn();
隐式绑定

调用对象中的方法,该方法中this指向对象本身

let obj = {
    count:1,
    getCount(){
        console.log(this);//obj
        console.log(this.count);//1
    }
}
obj.getCount();

上述代码相当于

let obj = {
    count:1,
    getCount(){
        console.log(obj);//obj
        console.log(obj.count);//1
    }
}
obj.getCount();
显式绑定

通过call(),apply(),bind(),强制改变this指向

let count = 2
let obj = {
    count:1,
    getCount(){
        console.log(this.count);//2
    }
}

obj.getCount.call(this);
let obj1 = {
    a:'obj1’
},
 obj2 = {
     a:'obj2'
 }
function fn(){
    console.log(this.a);
}

fn.call(obj1);//obj1
fn.call(obj2);//obj2

let obj = {
    a:"obj"
}
let a = 'a'
function fn (param){
    console.log(this.a);
    console.log(param);
}
fn()//a
fn.call(obj,'call')//obj,call
fn.apply(obj,['apply'])// obj , ['apply']

let fnBind = fn.bind(obj);
fnBind('bind');//obj , 'bind'


call(),apply(),bind()均可以改变this指向

call(),apply()bind()的区别

fn.bind(obj)时,bind方法会返回一个已经绑定this新函数。需要单独再次调用都会执行

let obj = {
    name:'bind'
}
function fn (){
    console.log(this.name);
}
let fn1 = fn.bind(obj);
fn1();//bind

call(),apply()绑定this,并执行该方法

let obj = {
    name:'obj',
    getName(){
        console.log(this.name);
    }
}

let obj1 = {
    name:'call'
}
let obj2 = {
    name:'bind'
}

obj.getName.apply(obj2)//bind
obj.getName.call(obj1);//call
apply()call()的区别

两者在于传递的参数形式不同,apply()接收的参数为数组形式

new绑定

函数或者方法是使用new调用,它就当成构造函数调用。

function Fn (){
    this.name = "fn";
}
let f = new Fn();
console.log(f);//{name: "fn"}

一般使用构造函数不会使用return。new调用时,内部会创建一个this的对象将我们添加的属性挂到时this对象身上并返回。

如果在构造函数中return一个原始值,然后再进行new绑定,内部会忽略掉构造函数返回的原始值,自己创建一个对象并返回

function Fn(){
    this.name = "fn";
    return 1;
}
let f = new Fn();
console.log(f);//{name: "fn"}

如果构造函数返回一个对象时,new绑定时内部不再自行创建,而是直接将对象返回即可

function Fn(){
    this.name = "fn";
    return {
        name:'object',
        age:20
    }
}
let f = new Fn ();
console.log(f);//{name: "object", age: 20}
This绑定丢失

别名丢失隐式绑定

var name = 'window';
let obj = {
    name:'obj',
    getName(){
        return this.name;
    }
}

let getName = obj.getName;
console.log(getName());//'window'

回调丢失隐式绑定

const WITH_TIME = 1000;
let name = "window";
let obj = {
    name:"obj",
    getName(){
        console.log(this.name);
    }
}

setTimeout(obj.getName,WITH_TIME);//window
This丢失修复

显式bind()绑定this

var name = 'window';
let obj = {
    name:'obj',
    getName(){
        return this.name;
    }
}

let getName = obj.getName.bind(obj);
console.log(getName());//'obj'

es6剪头函数

const WITH_TIME = 1000;
let name = "window";
let obj = {
    name:"obj",
    getName(){
       setTimeout(()=>{
           console.log(this.name);//'obj'
       },WITH_TIME);
    }
}
obj.getName();

This绑定优先级
显式绑定VS隐式绑定
function getName(){
    console.log(this.name);
}
let obj1 = {
    name:"javaScipt",
    getName:getName
}
let obj2 = {
    name:'Python',
    getName:getName
}

obj1.getName();//'javaScipt'
obj2.getName();//'Python'

obj1.getName.call(obj2);//Python
obj2.getName.call(obj1);//javaScipt

结论:显式绑定优先级大于隐式绑定

隐式绑定VSnew绑定
function getName(){
    console.log(this.name);
}
let obj1 = {
    name:"javaScipt",
    getName:getName
}
let obj2 = {
    name:'Python',
    getName:getName
}

obj1.getName();//'javaScipt'
obj2.getName();//'Python'

let o1 = new obj1.getName();//undefined
let o2 = new obj2.getName();//undefined

结论:new绑定优先级大小隐式绑定

显式绑定VSnew绑定
function getName(){
    console.log(this.name);
}
let obj1 = {
    name:"javaScipt",
    getName:getName
}
let obj2 = {
    name:'Python',
    getName:getName
}

obj1.getName.call(obj2);//'Python'
obj2.getName.call(obj1);//'javaScitp'

let o1 = new obj1.getName.call(obj2);//obj1.getName.call is not a constructor
let o2 = new obj2.getName.call(obj1);//obj2.getName.call is not a constructor

直接报错了,显式绑定与new绑定不能共用

bind()呢?

function getName(){
    console.log(this.name);
}
let obj1 = {
    name:"javaScipt",
    getName:getName
}
let obj2 = {
    name:'Python',
    getName:getName
}

let f1 = obj1.getName.bind(obj2);
let f2 = obj2.getName.bind(obj1);
f1();//'Python'
f2();//'javaScitp'

let b1 =obj1.getName.bind(obj2);
let b2 = obj2.getName.bind(obj1);
let o1 = new b1();//undefined
let o2 = new b2();//undefined

bind()会返回一个改变this指向的函数,然后new调用,this又改变了

结论: new绑定优先级大于显式绑定

参考文章

13.精读《This带来的困惑》

往期文章

看完这篇,让您的js优雅一个档次

多个Vue项目如何配置nginx

重学Vue Components

前端Vue+axios实现阿里云oss文件上传(服务端签名直传)

前端奇技淫巧之js调试

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值