this_原型鏈_繼承

this相關(guān)問題

apply、call 、bind的作用以及區(qū)別

call、apply和bind方法的用法以及區(qū)別

以下代碼輸出什么?
var john = { 
  firstName: "John" 
}
function func() { 
  alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()

輸出:John: hi!
解析:john.sayHi()可理解為john.sayHi.call(john),即this指向john,輸出即為John.firstName + ":hi!"

下面代碼輸出什么,為什么
func() 
function func() { 
  alert(this)
}

輸出:window對象
解析:func()可理解為func.call(null),瀏覽器里有一條規(guī)則:
如果你傳的 context 就 null 或者 undefined,那么 window 對象就是默認的 context(嚴格模式下默認 context 是 undefined)
因此上面的打印結(jié)果是 window。如果你希望這里的 this 不是 window,很簡單:

func.call(obj) // 那么里面的 this 就是 obj 對象了
下面代碼輸出什么
document.addEventListener('click', function(e){
    console.log(this);
    setTimeout(function(){
        console.log(this);
    }, 200);
}, false);

點擊頁面,依次輸出:document和window對象
解析:點擊頁面監(jiān)聽click事件屬于方法調(diào)用,this指向事件源DOM對象,即obj.fn.apply(obj),setTimeout內(nèi)的函數(shù)屬于回調(diào)函數(shù),可以這么理解,f1.call(null,f2),所以this指向window

下面代碼輸出什么,why
var john = { 
  firstName: "John" 
}

function func() { 
  alert( this.firstName )
}
func.call(john)

解析:call中已傳入第一個參數(shù)john,即this指向John
輸出:John

以下代碼有什么問題,如何修改
var module= {
  bind: function(){
    $btn.on('click', function(){
      console.log(this) //this指什么
      this.showMsg();
    })
  },
  
  showMsg: function(){
    console.log('hello');
  }
}

問題:this.showMsg()的this指向$btn,而$btn上沒有showMsg這個方法

解決辦法有多種:

  1. this.showMsg()改為module.showMsg()module.showMsg.call(module)
  2. 在事件監(jiān)聽的回調(diào)函數(shù)后綁定this,即this.showMsg();}.bind(this)),bind中的this指向的是module,所以直接寫bind(module)也可。
  3. 在異步操作之后this可能會發(fā)生改變,所以在這段代碼中,在事件執(zhí)行前將this的值保存為_this,最后通過_this.showMsg()調(diào)用方法,也可以得到正確結(jié)果
  4. 用ES6語法中的箭頭函數(shù),就不用寫方法2中糟心的代碼了,既然要改就全改了吧:
var module = {
    bind() {
        $btn.on('click', () => {
            console.log(this)
            this.showMsg();
        })
    },
    showMsg() {
        console.log('hello');
    }
}

原型鏈相關(guān)問題

有如下代碼,解釋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("Dot")
p.sayName();

關(guān)系:

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

前面講面向?qū)ο蟮牟┛屠?,我畫了這么張圖,放在這里也勉強合適:

繼承#禁止轉(zhuǎn)載

p是Person構(gòu)造函數(shù)的實例,p首先會查找自身有沒有toString()這個方法,顯然是沒有的,所以會順著proto原型鏈逐級向上查找,直到Object.prototype為止,如果還沒有找到就返回null,期間找到了就調(diào)用該方法。

記?。?/p>

  • 當 new 一個構(gòu)造函數(shù)的時候會創(chuàng)建一個實例,構(gòu)造函數(shù).prototype === 實例.__proto__
  • 一切函數(shù)都是由 Function 這個函數(shù)創(chuàng)建的,所以Function.prototype === 被創(chuàng)建的函數(shù).__proto__
  • 一切函數(shù)的原型對象都是由 Object 這個函數(shù)創(chuàng)建的,所以Object.prototype === 一切函數(shù).prototype.__proto__

表述關(guān)系為:

  • p.__proto__===Person.prototype,找到構(gòu)造函數(shù)的原型,沒有toString()方法于是繼續(xù)查找
  • p.__proto__.__proto__===Object.prototype,找到Object的原型對象
  • p.__proto__.__proto__.toString()===Object.prototype.toString()
    最后在Object.prototype中找到了toString()方法,所以此時就可以調(diào)用toString()方法了,這也叫做繼承方法。

原型鏈:
每個構(gòu)造函數(shù)都有一個原型對象,原型對象都包含指向其構(gòu)造函數(shù)的指針,而實例都包含一個指向原型對象的proto指針,我們讓原型對象等于另一個類型的實例,此時的原型對象將包含一個指向另一個原型的指針,以此為依據(jù),層層推進,就構(gòu)成了實例與原型的鏈條,稱為原型鏈。
在訪問對象的屬性時,如果在對象本身中沒有找到,則會去原型鏈中逐級向上查找,找到則返回該屬性,如果遍歷整個鏈都沒有找到則返回undefined。
原型鏈一般實現(xiàn)為一個鏈表,這樣就可以按照一定的順序來查找,原型鏈是實現(xiàn)繼承的主要方法。

對String做擴展,實現(xiàn)如下方式獲取字符串中頻率最高的字符
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因為d 出現(xiàn)了5次

增加如下代碼:

String.prototype.getMostOften = function () {
    var res = this.split('')
        .reduce((acc, cur) => {
            if (acc[cur]) {
                acc[cur]++
                return acc
            } else {
                acc[cur] = 1
                return acc
            }
        }, {})

    var max = ['', 0]
    for (var key in res) {
        if (res[key] > max[1]) {
            max = [key, res[key]]
        }
    }
    console.log(max)
    return max[0]
}
instanceOf有什么作用?內(nèi)部邏輯是如何實現(xiàn)的?
  • 作用:判斷一個對象是不是某個類型的實例
  • 實現(xiàn):A instanceof B的判斷規(guī)則是:沿著A的proto這條線來找,同時沿著B的prototype這條線來找,如果兩條線逐級向上查找能找到同一個引用,返回true,證明A是B類型的實例,否則返回false。
    代碼如下:
function instance(obj, type) {
    while (obj.__proto__) {
        // 以下判斷條件換為 obj.__proto__.constructor === type 也可
        if (obj.__proto__ === type.prototype) {
            return true
        } else {
            // 以下return語句換乘 obj = obj.__proto__ 也可
            return instance(obj.__proto__, type)
        }
    }
    return false
}

console.log(instance([], Array))
console.log(instance(/.\d/, RegExp))
console.log(instance({}, Object))

繼承相關(guān)問題

繼承有什么作用?

繼承機制使得不同的實例可以共享構(gòu)造函數(shù)的原型對象上的屬性和方法,提高了代碼的復用性。

下面兩種寫法有什么區(qū)別?
//方法1
function People(name, sex){
    this.name = name;
    this.sex = sex;
    this.printName = function(){
        console.log(this.name);
    }
}
var p1 = new People('Dot', 2)

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

Person.prototype.printName = function(){
    console.log(this.name);
}
var p1 = new Person('Dot', 2);

首先要知道構(gòu)造函數(shù)里定義的都是實例的屬性和方法。
方法1和方法2的區(qū)別在于printName方法所在的位置,方法1中的printName方法是實例的方法,也就是說每生成一個實例之后,實例的printName就會占用內(nèi)存;方法2中的printName方法定義在構(gòu)造函數(shù)的原型對象上(前面說過的實例.__proto__ === 構(gòu)造函數(shù).prototype),生成的所有實例都會共享原型對象上的所有方法,節(jié)省內(nèi)存,這也印證了一個結(jié)論:公共方法寫在原型對象上比較好。

Object.create 有什么作用?兼容性如何?
  • 作用:Object.create()接收兩個參數(shù),作用是創(chuàng)建接收到的第一個參數(shù)的副本,第二個參數(shù)是可選的、額外傳入副本里的屬性,以第二個參數(shù)指定的任何屬性都會傳入副本中并覆蓋已有的同名屬性,但原型對象上的同名屬性不會被改變。
    也就是說使用此方法時是先clone再在子類上添加自己的屬性和方法,以此實現(xiàn)原型式繼承。
    有代碼如下:
var person = {
    name: 'dot',
    friends: ['a', 'b', 'c']
}

var anotherPerson = Object.create(person, {
    name: {
        value: 'dolby'
    }
})

console.log(anotherPerson.name)//dolby
console.log(person.name)//dot
  • 兼容性:各大瀏覽器的最新版本(包括IE9)都部署了這個方法,可以寫一個polyfill解決低版本瀏覽器問題:
if(!Object.create){
  Object.create = function(obj){
    function F(){}
    F.prototype = obj
    return new F()
  }
}
hasOwnProperty有什么作用? 如何使用?
  • 作用:檢測一個屬性到底存在于原型中還是實例中,這個方法從Object繼承得來,只有在屬性存在于實例中才返回true
  • 使用:實例.hasOwnProperty('屬性名'),返回true則屬性存在于實例中,false則屬性存在于原型中。
function Person() { }

Person.prototype = {
    name: 'dot',
    sex: 'female',
    age: 2,
    sayName() {
        console.log(this.name)
    }
}

var person1 = new Person()
var person2 = new Person()

console.log(person1.hasOwnProperty('name'))//false

person1.name = 'dolby'
console.log(person1.name)//dolby,來自實例
console.log(person1.hasOwnProperty('name'))//true

console.log(person2.name)//dot,來自原型
console.log(person2.hasOwnProperty('name'))//false

delete person1.name//刪除實例屬性,恢復實例與原型的連接
console.log(person1.name)//dot,來自原型
console.log(person1.hasOwnProperty('name'))//false
如下代碼中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;
}

作用:借用構(gòu)造函數(shù)實現(xiàn)對實例屬性的繼承,這樣既實現(xiàn)了函數(shù)復用,又保證每個實例具有自己的屬性。本例中將this指向Person,實現(xiàn)在Male中繼承Person的屬性

補全代碼,實現(xiàn)繼承
function Person(name, sex){
    // todo ...
}

Person.prototype.getName = function(){
    // todo ...
};    

function Male(name, sex, age){
   //todo ...
}

//todo ...
Male.prototype.getAge = function(){
    //todo ...
};

var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();
function inherit(superType, subType) {
    var _prototype = superType.prototype
    _prototype.constructor = subType// 修改constructor指向
    subType.prototype = _prototype
}

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

Person.prototype.getName = function () {
    console.log(this.name)
}

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

inherit(Person, Female)// Female繼承Person

// 在繼承函數(shù)之后寫自己的方法,否則會被覆蓋
Female.prototype.getAge = function () {
    console.log(this.age)
}

var dot = new Female('Dot', '女', 2)
dot.getName()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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