this、原型鏈、繼承

深入詳解javascript之delete操作符
深入javascript(六):instanceof 運(yùn)算符
重新理解JS的6種繼承方式
JS 的 new 到底是干什么的?

1.apply、call 、bind有什么作用,什么區(qū)別

  • 這三個(gè)函數(shù)主要作用在于改變this的指向。

  • call:傳遞一個(gè)thisArgs參數(shù)和一個(gè)參數(shù)列表,thisArgs 指定了函數(shù)在運(yùn)行期的調(diào)用者,也就是函數(shù)中的 this 對象,而參數(shù)列表會(huì)被傳入調(diào)用函數(shù)中,如:

    var a = {
    
         name:'onepixel', //定義a的屬性
    
        say:function(){ //定義a的方法
            console.log("Hi,I'm function a!");
        }
    };
    
    function b(name){
        console.log("Post params: "+ name);
        console.log("I'm "+ this.name);
        this.say();
    }
    
    b.call(a,'test');
    >>>
    Post params: test
    I'm onepixel
    I'm function a!
    
  • apply:和call效果相似,只是第二個(gè)參數(shù)傳遞的是數(shù)組

     function b(x,y,z){
       console.log(x,y,z);
     }
     b.apply(null,[1,2,3]); // 1 2 3
    
  • bind:ES5引入 bind 的真正目的是為了彌補(bǔ) call/apply 的不足,由于 call/apply 會(huì)對目標(biāo)函數(shù)自動(dòng)執(zhí)行,從而導(dǎo)致它無法在事件綁定函數(shù)中使用,因?yàn)槭录壎ê瘮?shù)不需要我們手動(dòng)執(zhí)行,它是在事件被觸發(fā)時(shí)由JS 內(nèi)部自動(dòng)執(zhí)行的。而 bind 在實(shí)現(xiàn)改變函數(shù) this 的同時(shí)又不會(huì)自動(dòng)執(zhí)行目標(biāo)函數(shù)
    var obj = {name:'onepixel'};

    /**
    * 給document添加click事件監(jiān)聽,并綁定onClick函數(shù)
    * 通過bind方法設(shè)置onClick的this為obj,并傳遞參數(shù)p1,p2
    */
     document.addEventListener('click',onClick.bind(obj,'p1','p2'),false);
    
     //當(dāng)點(diǎn)擊網(wǎng)頁時(shí)觸發(fā)并執(zhí)行
     function onClick(a,b){
         console.log(
               this.name, //onepixel
               a, //p1
               b  //p2
         )
     }  
    

2.以下代碼輸出什么?

  var john = { 
    firstName: "John" 
  }
  function func() { 
    alert(this.firstName + ": hi!")
  }
  john.sayHi = func
  john.sayHi()   //john:hi

3.下面代碼輸出什么,為什么

  func() 
  function func() { 
    alert(this)
  }    //代碼輸出為window
  • 因?yàn)閒unc函數(shù)為全局變量,相當(dāng)于window.func() 或者是 func.call(undefined)

4.下面代碼輸出什么

  document.addEventListener('click', function(e){
      console.log(this);    //  這個(gè)this為document
      setTimeout(function(){
          console.log(this);    // setTimeout、setInterval函數(shù)的全局對象為window
      }, 200);
  }, false);

5.下面代碼輸出什么,why

  var john = { 
      firstName: "John" 
  }

  function func() { 
      alert( this.firstName )
  }
  func.call(john)    //  John
  • 因?yàn)槭褂昧薱all方法,該方法將func的this轉(zhuǎn)向?yàn)閖ohn

6.以下代碼有什么問題,如何修改

  var module= {
    var _this = this   //  將this賦值給_this
    bind: function(){
      $btn.on('click', function(){
        console.log(this)   //  this 指 $btn
        this.showMsg();    //  修改為_this.showMsg
      })
    },

    showMsg: function(){
      console.log('饑人谷');
    }
  }

7.有如下代碼,解釋Person、 prototype、proto、p、constructor之間的關(guān)聯(lián)。

   function Person(name){
       this.name = name;
   }
   Person.prototype.sayName = function(){
       console.log('My name is :' + this.name);
   }
   var p = new Person("若愚")
   p.sayName();
  • Person是一個(gè)構(gòu)造函數(shù),當(dāng)new Person()的時(shí)候會(huì)創(chuàng)建一個(gè)空對象,然后將空對象的__proto__指向prototype,再將返回值賦值給p(實(shí)例對象),對象的constructor屬性用于返回創(chuàng)建該對象的函數(shù),也就是我們常說的構(gòu)造函數(shù)

8.上例中,對對象 p可以這樣調(diào)用 p.toString()。toString是哪里來的? 畫出原型圖?并解釋什么是原型鏈。

  • p.toString()方法是繼承構(gòu)造函數(shù)Object的原型對象里定義的toString方法,首先p會(huì)找自己的toString方法,如果沒有找到,會(huì)沿著__proto__屬性繼續(xù)到構(gòu)造函數(shù)Person的prototype里找toString方法,如果還未找到,再繼續(xù)往Person.prototype的__proto__即Object.prototype找toString方法,最后找到toString()方法。
  • 原型鏈:由于原型對象本身也是對象,而每個(gè)javascript對象都有一個(gè)原型對象,每個(gè)對象都有一個(gè)隱藏的proto屬性,原型對象也有自己的原型,而它自己的原型對象又可以有自己的原型,這樣就組成了一條鏈,這個(gè)就是原型鏈。在訪問對象的屬性時(shí),如果在對象本身中沒有找到,則會(huì)去原型鏈中查找,如果找到,直接返回值,如果整個(gè)鏈都遍歷且沒有找到屬性,則返回undefined。原型鏈一般實(shí)現(xiàn)為一個(gè)鏈表,這樣就可以按照一定的順序來查找。
3704824-d6cbc01eac70a205.png

9.對String做擴(kuò)展,實(shí)現(xiàn)如下方式獲取字符串中頻率最高的字符

    // 方法一: 
    String.prototype.getMostOften = function(){
        var obj = {};
        for(var i = 0; i < this.length; i++){
            if(obj[this[i]]){
                obj[this[i]]++;
            }else{
                obj[this[i]] = 1;
            }
        }

        var count = 0,key;
        for(var k in obj){
            if(obj[k] > count){
                count = obj[k];
                key = k
            }
        }

        return key + ',出現(xiàn)次數(shù):' + count
    }
    
    var str = 'ahbbccdeddddfg';
    var ch = str.getMostOften();
    console.log(ch); //d , 因?yàn)閐 出現(xiàn)了5次

    //  方法二:
    String.prototype.getMostOften = function(){
        var arr = this.split('');
        var obj = arr.reduce(function(init,value){
            if(init[value]){
                init[value]++;
            }else{
                init[value] = 1;
            }

            return init;
        },{})

        var count = 0,key;
        for(var k in obj){
            if(obj[k] > count){
                count = obj[k];
                key = k;
            }
        }

        return key + ',出現(xiàn)次數(shù):' + count
    }

    
    
    var str = 'ahbbccdeddddfg';
    var ch = str.getMostOften();
    console.log(ch);

10.instanceOf有什么作用?內(nèi)部邏輯是如何實(shí)現(xiàn)的?

instanceof運(yùn)算符可以用來判斷某個(gè)構(gòu)造函數(shù)的prototype屬性是否存在另外一個(gè)要檢測對象的原型鏈上。

  function instanceOf(obj,fn){
    var oldpro = obj.__proto__;
    while(oldpro){
      if(oldpro === fn.prototype){
          return true;
          break;
      }else{
          oldpro = oldpro.__proto__;
      }
    }
    return false;
  }

11.繼承有什么作用?

  • 概念:繼承是指一個(gè)對象直接使用另一個(gè)對象的屬性和方法。
  • 作用:繼承劃分了類的層次性,父類代表的是更一般、更泛化的類,而子類則是更為具體、更為細(xì)化;繼承是實(shí)現(xiàn)代碼重用、擴(kuò)展軟件功能的重要手段,子類中與父類完全相同的屬性和方法不必重寫,只需寫出新增或改寫的內(nèi)容,這就是說子類可以復(fù)用父類的內(nèi)容,不必一切從零開始

12.下面兩種寫法有什么區(qū)別?

  //方法1
  function People(name, sex){
      this.name = name;
      this.sex = sex;
      this.printName = function(){
          console.log(this.name);
      }
  }
  var p1 = new People('饑人谷', 2)

  //方法2
  function Person(name, sex){
      this.name = name;
      this.sex = sex;
  }

  Person.prototype.printName = function(){
      console.log(this.name);
  }
  var p1 = new Person('若愚', 27);
  • 區(qū)別:同樣都是創(chuàng)建printName方法,方法1的printName方法是在函數(shù)Person實(shí)例對象里的,方法2是在Person的prototype對象上的。當(dāng)再創(chuàng)建一個(gè)Person實(shí)例對象的時(shí)候,方法1又將會(huì)再創(chuàng)建一個(gè)printName方法,占用新的內(nèi)存,而方法2將一個(gè)公用的printName方法寫在原型上,當(dāng)對象要使用該方法只需到原型鏈里調(diào)用就可以了,達(dá)到節(jié)省內(nèi)存的效果

13.Object.create 有什么作用?兼容性如何?

  • Object.create() 方法使用指定的原型對象和其屬性創(chuàng)建了一個(gè)新的對象。

  • 不支持IE8以下瀏覽器

    function Person(name, age){
        this.name = name;
        this.age = age;
    }
    Person.prototype.sayName = function(){
        console.log(this.name);
    }
    function Male(name, age, sex){
        Person.call(this, name, age);
        this.sex = sex;
    }
    // Male.prototype = new Person(); //該方法同下,代替不兼容Object.create()的使用場景
    Male.prototype = Object.create(Person.prototype);
    Male.prototype.constructor = Male;
    Male.prototype.sayAge = function(){
          console.log(this.age);
    };
    var p1 = new Male('hunger', 20, 'nan');
    p1.sayName();//hunger
    p1.sayAge();//20
    

14.hasOwnProperty有什么作用? 如何使用?

  • hasOwnPerperty是Object.prototype的一個(gè)方法,可以判斷一個(gè)對象是否包含自定義屬性而不是原型鏈上的屬性,hasOwnProperty是JavaScript中唯一一個(gè)處理屬性但是不查找原型鏈的函數(shù)

  • 此方法不會(huì)檢查對象的原型鏈中是否存在該屬性,該屬性只有是對象本身的一個(gè)成員才會(huì)返回true

    function Site(){
        this.name = "CodePlayer";
        this.url = "http://www.365mini.com/";
    
        this.sayHello = function(){
            document.writeln("歡迎來到" + this.name);
        };
    }
    
    var obj = {
        engine: "PHP",
        sayHi: function(){
            document.writeln("歡迎訪問" + this.url);
        }
    };
    // 使用對象obj覆蓋Site本身的prototype屬性
    Site.prototype = obj;
    
    var s =  new Site();
    document.writeln( s.hasOwnProperty("name") ); // true
    document.writeln( s.hasOwnProperty("sayHello") ); // true
    // 以下屬性繼承自原型鏈,因此為false
    document.writeln( s.hasOwnProperty("engine") ); // false
    document.writeln( s.hasOwnProperty("sayHi") ); // false
    document.writeln( s.hasOwnProperty("toString") ); // false
    
    // 想要查看對象(包括原型鏈)是否具備指定的屬性,可以使用in操作符
    document.writeln( "engine" in s ); // true
    document.writeln( "sayHi" in s ); // true
    document.writeln( "toString" in s ); // true
    

15.如下代碼中call的作用是什么?

  function Person(name, sex){
      this.name = name;
      this.sex = sex;
  }
  function Male(name, sex, age){
      Person.call(this, name, sex);    //這里的 call 有什么作用
      this.age = age;
  }
  • call的作用是將構(gòu)造函數(shù)Person的this指向Male,使得Male擁有Person的屬性

16.補(bǔ)全代碼,實(shí)現(xiàn)繼承

    function Person(name, sex){
        this.name = name;
        this.sex = sex;
    }

    Person.prototype.getName = function(){
        console.log('My name is' + this.name)
    };    

    function Male(name, sex, age){
       Person.call(this,name,sex);
       this.age = age;
    }

    Male.prototype = Object.create(Person.prototype);
    Male.prototype.constructor = Male
    // 兼容寫法:
    // function Temp(){}
    // Temp.prototype = Person.prototype
    // Male.prototype = new Temp()

    Male.prototype.getAge = function(){
        console.log('My age is' + this.age)
    };
    var ruoyu = new Male('若愚', '男', 27);
    ruoyu.printName();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容