最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 原生js中的三座大山(上)-this

    正文概述 掘金(布灵布灵)   2021-05-07   429

    前言:

    想要学好前端,基本功必须得要扎实,原型和原型链、闭包、this,那这三座大山就一定要跨过去,今天先说this,话不多说,go~

    this:

    在开发时,我们要搞清楚this的指向,是至关重要的~

    为了能够一眼看出this指向的是什么,我们需要确定它的绑定规则是哪个?this有四种绑定规则:

    • 默认绑定
    • 隐式绑定
    • 显示绑定
    • new绑定
    默认绑定

    无法应用其他规则时就应用默认绑定,经常是独立函数调用

    function foo(){
    	console.log(this.a);
    }
    var a = 3;
    foo();//3
    

    上面函数就应用了this的默认绑定,foo()前面没有调用它的对象,其实也可以这么想,window.foo(),window可以省略,所以foo()里面的this指向全局对象window(非严格模式下),严格模式下,this指向undefined,undefined上没有this对象,会抛出错误。

    隐式绑定

    考虑调用位置是否有上下文对象

    function foo(){
        console.log(this.a);
    }
    var obj = {
        a:2,
        foo:foo
    }
    obj.foo();//2
    

    因为调用foo的时候前面加上了obj,所以隐式绑定会把函数调用中的this绑定到这个上下文对象,也就是obj,所以this.a 和obj.a是一样的 但是,你需要记住一句话:this永远指向最后调用它的那个对象

    function foo(){
        console.log(this.a);
    }
    var obj1 = {
        a:2,
        foo:foo
    }
    var obj2 = {
        a:3,
        obj1:obj1
    }
    obj2.obj1.foo();//2
    

    可见,结果是2,因为this指向最后调用它的那个对象,即obj1

    在使用隐式绑定的时候,有一个常见的问题,就是会出现隐式丢失

    隐式丢失

    也就是说出现了隐式丢失,会应用默认绑定的规则

    function foo(){
        console.log(this.a);
    }
    var obj = {
        a:2,
        foo:foo
    }
    var baz = obj.foo;
    var a = '我是全局对象的a';
    baz();//我是全局对象的a
    

    因为baz()其实是一个独立函数调用,所以应用了默认绑定

    还有一种更常见的情况会发生隐式丢失,那就是在传入回调函数时:

    function foo(){
        console.log(this.a);
    }
    function baz(fn){
        fn()
    }
    var obj = {
        a:2,
        foo:foo
    }
    var a = '我是全局对象的a';
    baz(obj.foo);//我是全局对象的a
    

    参数传递其实就是一种隐式赋值

    显示绑定

    所谓显示,是因为你可以直接指定this的绑定对象,我们可以借助apply,call,bind等方法

    apply和call作用一样,只是传参的方式不同,都会执行对应的函数,但是bind不一样,它不会执行,需要手动去调用

    function foo(){
     	console.log(this.a);
    }
    function baz(fn){
      	fn()
    }
    var obj = {
     	 a:2,
    }
    
    foo.call(obj);//2
    foo.apply(obj);//2
    

    但是,依然无法解决丢失绑定的问题,如下:

    function sayHi(){
         console.log('Hello,', this.name);
     }
     var per = {
         name: 'mengyun',
         sayHi: sayHi
     }
     var name = 'anna';
     var Hi = function(fn) {
         fn();
     }
     Hi.call(per, per.sayHi); //Hello, anna
    

    但是我们可以给fn也硬绑定this,就可以解决这个问题

    function sayHi(){
        console.log('Hello,', this.name);
    }
    var per = {
        name: 'mengyun',
        sayHi: sayHi
    }
    var name = 'anna';
    var Hi = function(fn) {
        fn.call(this);
    }
    Hi.call(per, per.sayHi); //Hello, mengyun
    

    原因:因为per被绑定到Hi函数中的this上,fn又将这个对象绑定给了sayHi的函数。这时,sayHi中的this指向的就是per对象。

    上面代码用bind可以改写成:(bind在你不知道的js(上)中就是被归为硬绑定)

    function sayHi(){
        console.log('Hello,', this.name);
    }
    var per = {
        name: 'mengyun',
        sayHi: sayHi
    }
    var name = 'anna';
    var Hi = sayHi.bind(per);
    Hi.call(per, per.sayHi); //Hello, mengyun
    
    new绑定

    js跟其他语言不一样,没有类,所以我们可以用构造函数来模拟类

    使用new来调用函数,会自动执行下面的操作:

    1. 创建一个全新的对象
    2. 这个对象会被执行[[Prototype]]连接
    3. 这个新对象会绑定到函数调用的this
    4. 如果函数没有返回其他对象,那么返回这个新对象,否则返回构造函数返回的对象
    手写一个new
    function _new(fn,...args){
      //1、创建一个空对象
      //2、这个对象的 __proto__ 指向 fn 这个构造函数的原型对象
      var obj = Object.create(fn.prototype);
      //3、改变this指向
      var res = fn.apply(obj,args);
      // 4. 如果构造函数返回的结果是引用数据类型,则返回运行后的结果,否则返回新创建的 obj
      if ((res!==null && typeof res == "object") || typeof res == "function") {
          return res;
      }
      return obj;
    }
    

    下面我们用new来绑定一下this,

    function Person(name){
    	 this.name = name;
    }
    var per = new Person('mengyun');
    console.log(per.name);//mengyun
    

    此时per已经被绑定到Person调用中的this上

    优先级

    在知道了四种绑定规则之后,我们需要的就是准确判断是哪种绑定规则,但是如果某个调用位置可以应用多条规则,那么就需要有个优先级了。 毫无疑问,默认绑定是最低的,可以先不考虑他,下面来看个例子:

    function f1(){
       console.log(this.a);
    }
    var obj1 = {
       a:2,
       f1:f1
    };
    var obj2 = {
       a:3,
       f1:f1
    };
    
    obj1.f1();//2
    obj2.f1();//3
    
    obj1.f1.apply(obj2);//3
    

    可以看出来,显示绑定优先级大于隐式绑定 其他的demo大家可以自行去写,最后的结论就是 new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

    绑定例外

    如果把null或者undefined作为绑定对象传入call,apply,bind,这些值往往会被忽略,实际应用的是默认绑定规则:

    var obj= {
        name: 'mengyun'
    }
    var name = 'Anna';
    function bar() {
        console.log(this.name);
    }
    bar.call(null); //Anna
    

    注意: 但是以上规则只能应用在普通函数上,箭头函数例外,因为它没有自己的this,而是根据外层作用域来决定的,而且箭头函数的绑定无法被修改

    function foo(){
        return (a)=>{
            console.log(this.a);
        }
    }
    var obj1 = {
        a:2
    };
    var obj2 = {
        a:3
    };
    var baz = foo.call(obj1);
    baz.call(obj2);//2
    

    由于foo()的this被绑定到obj1,所以baz()也被绑定到obj1,并且无法被修改


    起源地下载网 » 原生js中的三座大山(上)-this

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元