JS面向?qū)ο缶幊?3):原型鏈與繼承

1.以下代碼輸出什么?

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

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

2.以下代碼輸出什么?

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

輸出window對象。func()相當(dāng)于func.call(undefined),故輸出window對象。若要改變this指向,則應(yīng)寫成:func.call(obj)

3.下面代碼輸出什么?

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

單擊頁面,依次輸出document對象、window對象。
DOM對象綁定事件時,傳入的函數(shù)中的this綁定到該DOM對象
setTImeout 和 setInterval 這兩個方法執(zhí)行的函數(shù)中的this綁定到全局對象window。

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

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

在事件監(jiān)聽函數(shù)中 this.showMsg(), this指代 $btn 這個jQuery對象,并不代表module對象,因此 this.showMsg() 相當(dāng)于 $btn.showMsg()。
想要在事件監(jiān)聽函數(shù)中 調(diào)用module 的方法,可以人為把this綁定到module對象,使用 bind() 即可:

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

也可以將module的this保存起來為that,最后用that.showMsg()去執(zhí)行。
也可以用ES6的箭頭函數(shù)。

5.有如下代碼,解釋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("Li")
p.sayName();
  • 首先,Person是一個構(gòu)造函數(shù),而p是它的一個實例。
  • 故,p.__proto__ == Person.prototype
  • prototype對象有一個constructor屬性,指向prototype對象所在的構(gòu)造函數(shù)。故Person.prototype.constructor指向Person,p.__proto__.constructor也指向Person。

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

原型圖

如圖所示,p自身沒有toString,就到Person類的prototype去找,也沒有,prototype是個對象,由Object類創(chuàng)建,但是仍然沒有toString,所以繼續(xù)到prototype的_ proto _ 也就是Object的prototype中去找,最終找到了toString方法。
由此引出原型鏈的概念:
每個對象都有一個proto屬性,其指向該對象的原型對象,而原型對象也是對象,所以也有一個proto指向原型對象的原型對象,這樣就組成了一條鏈,稱為原型鏈。在訪問對象的屬性時,如果在對象本身中沒有找到,則會去原型鏈中查找,如果找到,直接返回值。
原型鏈?zhǔn)菍崿F(xiàn)繼承的主要方法。

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

var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因為d 出現(xiàn)了5次

代碼如下:

String.prototype.getMostOften = function(){
  var obj = {};
  var max = 0;
  var result;
  for(var i=0; i<this.length; i++){
    if(obj[str[i]]){
      obj[str[i]] += 1;
    } else {
      obj[str[i]] = 1;
      }
 }
  for(var key in obj){
     if(obj[key]>max){
        max = obj[key];
        result = key;
     } 
  }
  
  return result;
};

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

instanceOf運算符可以判斷一個對象是否是某個類的實例。
如果一個對象通過沿著 _ proto _ 組成的原形鏈一層層查找其constructor屬性,如果有一層proto等于某類的prototype,則該對象是某類型的實例。

function instance(obj,fun){
  if(obj.__proto__){
    if(obj.__proto__ === fun.prototype){
      return true;
    } else {
         return instance(obj.__proto__,fun);
      }
  }
  return false;
}

console.log(instance([],Array));  //true
console.log(instance([],Object));  //true
console.log(instance([],String));   //false

9.繼承有什么用?

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

10. 下面兩種寫法有什么區(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);

同樣是綁定一個printName方法,前者直接將方法寫在構(gòu)造函數(shù)上,而后者將方法寫在構(gòu)造函數(shù)的prototype對象上,前者在每生成一個實例之后實例的printName就占用內(nèi)存,而后者每生成一個實例后會共享構(gòu)造函數(shù)prototype對象上的printName方法,以達(dá)到節(jié)省內(nèi)存的效果,也便于后期維護。

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

  • 該方法接受一個對象作為參數(shù),然后以它為原型,返回一個實例對象。該實例完全繼承原型對象的屬性。還可傳入第二個參數(shù),該參數(shù)是一個屬性描述對象,它所描述的對象屬性,會添加到實例對象,作為該對象自身的屬性。
  • Object.create是ES5中的方法,各大瀏覽器的最新版本(包括IE9)都部署了這個方法。如果老式瀏覽器不支持Object.create方法,可以就用這段代碼自己部署。
if(typeof Object.create !== 'function'){
   Object.create = function(obj){
      function F(){}
      F.prototype = obj;
     return new F();
   };
}

以上代碼說明,Object.create方法的實質(zhì)是新建一個構(gòu)造函數(shù)F,使其prototype屬性指向參數(shù)對象obj,最后返回一個F的實例,從而讓該實例繼承obj對象的屬性。

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

hasOwnProperty方法用于返回一個布爾值,用于判斷某個屬性定義在對象自身,還是定義在原型鏈上。

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

Person.prototype.printName = function(){
    console.log(this.name);
}
var p1 = new Person('Li', 23)
p1.hasOwnProperty('name')  //true
p1.hasOwnProperty('printName')  //false

13.如下代碼中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的作用是,指定Person函數(shù)執(zhí)行上下文中的this為當(dāng)前的Male創(chuàng)建的新對象。這樣通過Male構(gòu)造出的對象,便能繼承擁有Person中的屬性,也就是name 和sex。

14.補全代碼,實現(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 li = new Male('Li', '男', 24);
li.printName();

代碼如下:

function Person(name,age){
  this.name = name;
  this.age = age;
}
Person.prototype.printName = function(){
    console.log(this.name);
};

function Male(name,age,sex){
  this.sex = sex;
  Person.call(this,name,age);
}
Male.prototype = Object.create(Person.prototype);
Male.prototype.constructor = Male;
Male.prototype.printSex = function(){
  console.log(this.sex);
};

var a = new Male('Li',24,'male');
a.printName();  //Li

也可封裝成一個函數(shù)使用:

function inherit(superType,subType){
  var _superType = Object.create(superType.prototype);
  _superType.constructor = superType;
  subType.prototype = _superType;
}

function Person(name,age){
  this.name = name;
  this.age = age;
}
Person.prototype.printName = function(){
    console.log(this.name);
};

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

inherit(Person,Male);

Male.prototype.printSex = function(){
  console.log(this.sex);
};
var a = new Male('Li',24,'male');
a.printName();  //Li

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

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

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