this&原型鏈&繼承 · 問答

weixin_34321977發表於2017-08-01

this 相關問題

問題1: apply、call 、bind有什麼作用,什麼區別

共同點:都可以改變函式執行的上下文

不同點:
bind()方法會建立一個新的函式, 當被呼叫時,將其this關鍵字設定為提供的值,在呼叫新函式時,在任何提供之前提供一個給定的引數序列。
而call和apply是在函式呼叫時使其具有一個指定的this值,沒有建立新函式。

call和apply的返回值是:指定的this值和引數。
bind的返回值是:指定的this值和初始化引數改造的原函式拷貝。

call和apply的唯一區別就是call()方法接受的是若干個引數的列表,而apply()方法接受的是一個包含多個引數的陣列。
call中的引數需要依次傳入
例如:fn.call(context, param1, param2, param3)
apply中的引數則是以陣列的形式傳入
例如:fn.apply(context, [param1, param2, param3])或
var paramArray = [param1, param2, param3];
fn.apply(context, paramArray)

問題2: 以下程式碼輸出什麼?

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

會輸出John: hi!

john.sayHi()
形式類似於:
obj.child.method.call(obj.child, p1, p2)
可以轉換為:
john.sayHi().call(john)
所以this.firstName就是john.firstName

問題3: 下面程式碼輸出什麼,為什麼

func() 
function func() { 
  alert(this)
}

會輸出window物件
func()可以轉化為
func.call(undefined)
在瀏覽器中,如果context為undefined或者null那麼預設context為window物件

問題4:下面程式碼輸出什麼

document.addEventListener('click', function(e){
    console.log(this);
    setTimeout(function(){
        console.log(this);
    }, 200);
}, false);

點選document物件後,先輸出document物件,兩秒後輸出window物件

首先在事件處理程式中this代表事件源DOM物件
其次在setTimeout(還有setInterval)中,所有this都指向window物件

問題5:下面程式碼輸出什麼,why

var john = { 
  firstName: "John" 
}

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

會輸出John

func.call(john)指定了func函式的this為john物件
alert( john.firstName )

問題6: 以下程式碼有什麼問題,如何修改

var module= {
  bind: function(){
    $btn.on('click', function(){
      console.log(this) //this指什麼
      this.showMsg();
    })
  },
  
  showMsg: function(){
    console.log('飢人谷');
  }
}

bind函式的原意是給按鈕$btn加上監聽,點選後呼叫module的另一個方法showMsg輸出 '飢人谷' 。
但是在jQuery的事件處理程式中this都指向與select相匹配的元素,在這裡的this就是$btn。

修改:

var module= {
  bind: function(){
    var _this = this;
    $btn.on('click', function(){
      console.log(_this);
      _this.showMsg();
    })
  },
  
  showMsg: function(){
    console.log('飢人谷');
  }
}

原型鏈相關問題

問題7:有如下程式碼,解釋Person、 prototype、__proto__、p、constructor之間的關聯。

function Person(name){
    this.name = name;
}
Person.prototype.sayName = function(){
    console.log('My name is :' + this.name);
}
var p = new Person("若愚")
p.sayName();

Person是建構函式
p是Person構造的例項

建構函式Person的prototype和例項p的__proto__指向相同
Person.prototype == p.__proto__

Person原型的建構函式指向Person自己
Person.prototype.constructor == Person

問題8: 上例中,物件 p可以這樣呼叫 p.toString()。toString是哪裡來的? 畫出原型圖?並解釋什麼是原型鏈。

2998563-97793b4269cc30c5.png
原型鏈.png

首先p發現自己沒有toString()方法,於是p根據其屬性__proto__去原型Person.prototype中找。
發現還是沒有,於是Person.prototype根據其屬性__proto__去Object的原型中找。
發現有toString方法,於是p就可以呼叫toString()方法了。

原型鏈就是當一個物件發現自己沒有要呼叫的屬性或方法時,它會去自己的原型裡去找,若還是沒有就繼續去原型的原型裡找,直到找到這個屬性(或方法)或者當前物件的原型為null了才停止。

問題9:對String做擴充套件,實現如下方式獲取字串中頻率最高的字元

var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因為d 出現了5次
if(!String.hasOwnProperty('getMostOften')){ //先檢查一下原型裡有沒有這個方法
  String.prototype.getMostOften = function(){
    var array = this.split('');
    var mostOften = {};
    var mostNum = 0;
    var mostItem = '';
    for(var i = 0; i<array.length; i++){
      if(mostOften.hasOwnProperty(array[i])){
        mostOften[array[i]] += 1;
        if(mostOften[array[i]]>mostNum){
          mostNum = mostOften[array[i]];
          mostItem = array[i];
        }
      }else{
        mostOften[array[i]] = 0;
      }
    }
    return mostItem;
  }
}

問題10: instanceOf有什麼作用?內部邏輯是如何實現的?

instanceOf可以測試一個物件在其原型鏈中是否存在一個建構函式的 prototype 屬性

內部邏輯
就是沿著這個物件的原型鏈,一個一個去檢視有沒有和這個建構函式的prototype相等的原型,直到到達原型鏈的末尾。

function myInstanceOf(obj, constru){
  var objProto = obj.__proto__;
  while(objProto !== constru.prototype){
    objProto =objProto.__proto__;
    if(objProto == null){
      return false;
    }
  }
  return true;
}

繼承相關問題

問題11:繼承有什麼作用?

子類可以用父類的屬性和方法(重複使用)

  1. 共用原型的方法和屬性,節省記憶體
  2. 區分共用和私用的方法和屬性,構建物件之間合理的結構

問題12: 下面兩種寫法有什麼區別?

//方法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);

方法1每次建立一個例項都會重新給例項宣告一個新的printName方法
方法2把printName方法放到了Person原型中,這樣每次建立例項,不用重新宣告例項的printName方法
當例項呼叫printName方法時只需去Person的原型裡去呼叫。

問題13: Object.create 有什麼作用?相容性如何?

Object.create() 方法使用指定的原型物件和其屬性建立了一個新的物件。
可以使用Object.create實現類式繼承。

2998563-79e6dd7c2f32cf27.png
Object.create 相容性

問題14: hasOwnProperty有什麼作用? 如何使用?

hasOwnProperty()可以判斷一個物件是否包含自定義屬性而不是原型鏈上的屬性,hasOwnProperty是JavaScript中唯一一個處理屬性但是不查詢原型鏈的函式。

obj.hasOwnProperty(prop)
引數prop
要檢測的屬性字串名稱或者Symbol

返回值
用來判斷某個物件是否含有指定的屬性的Boolean

問題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的作用是讓Male類繼承Person類的name和sex屬性

問題16: 補全程式碼,實現繼承

function Person(name, sex){
    // todo ...
}

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

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

   Person.call(this, name, sex);
}

Male.prototype = Object.create(Person.prototype);
Male.prototype.constructor = Male;

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

var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();

相關文章