JS原型和原型鏈

構造函數

function Foo(name, age){
    this.name = name;
    this.age = age;
    this.class = 'class-1';
 }
var f =new Foo('zhangsan',20);

命名規則:大寫字母開頭;
那麼,在使用new操作符來調用一個構造函數的時候,發生了什麼呢?其實很簡單,就發生了四件事:

var obj  ={};
obj.__proto__ = Foo.prototype;
Foo.call(obj);
return obj;

第一行,創建一個空對象obj。

第二行,將這個空對象的proto成員指向了構造函數對象的prototype成員對象,這是最關鍵的一步,將新生成的對象的prop屬性賦值爲構造函數的prototype屬性,使得通過構造函數創建的所有對象可以共享相同的原型。

第三行,將構造函數的作用域賦給新對象,因此Foo函數中的this指向新對象obj,然後再調用Foo函數。於是我們就給obj對象賦值了。

第四行,返回新對象obj。

構造函數 - 擴展

var a = {} 其實是 var a = new Object()的語法糖;
var a = [] 其實是 var a = new Array() 的語法糖;
function Foo(){...} 其實是 var Foo = new Function(...)
》所有引用類型都有構造函數
》在實際開發過程中依然推薦使用左側的實現方式,易讀性、性能方面左側都更好;

原型規則和示例

① 原型規則
 所有除了“null”以外的引用類型(數組、對象、函數),都具有對象特性,既可自由擴展屬性
 所有除了“null”以外的引用類型(數組、對象、函數),都有一個proto 屬性(稱它爲‘隱式原型’,proto翻譯爲原型機、樣機),屬性值是一個普通的對象。附:之所以叫隱式,大概是因爲proto裏的方法是通過這種原型鏈的形式繼承過來的,自己沒定義卻可以使用。
 所有的函數,都有一個prototype(稱它爲‘顯式原型’prototype翻譯爲原型,雛形,藍本)屬性,屬性值也是一個普通的對象;
 所有引用類型的proto屬性值都指向它的構造函數的“prototype”屬性值
 當試圖得到一個對象的某個屬性時,如果這個對象本身沒有這個屬性,那麼回去它的proto(既它的構造函數的prototype)中尋找
② 關於this在此場景的一個使用說明,無論是對象(或其他引用類型)自身的屬性還是從原型中得到的屬性,屬性中用到的this都指向該對象自身
③ 循環對象自身的屬性

 var item
 for (item in f){
      // 高級瀏覽器已經在 for in 中屏蔽了來自原型的屬性
      // 但是這裏建議大家還是加上這個判斷,保證程序的健壯性
      if(f.hasOwnProperty(item)){
         // 如果是自身的屬性就輸出
         console.log(item); 
      }       
   }        

原型鏈

① 以調用toString()方法爲例
var f =new Foo('zhangsan',20); //通過構造方法Foo創建了一個對象
f.toString()
→對象f(或其他引用類型)中沒有此方法
→去f的proto中查找(即構造方法Foo.prototype中查找)
→仍然沒找到,而Foo.prototype本身是個對象,所去 Foo.prototype.proto中查找(即構造方法Object.prototype)
→找到了toString()方法,執行
② 注意:
如果Object.prototype仍然沒有的話,會繼續去Object.prototype.proto中查找;
不過js規定了Object.prototype.proto 返回的是null,這是特例,大概是防止這麼無限循環下去吧;
③ instanceof方法工作原理:
a)定義:用於判斷引用類型是否屬於某個構造函數
b)f instanceof Foo的判斷邏輯是:從f開始通過proto一層層往上,能否找到Foo.prototype (第一下就找到了,正確)
c)f instanceof Object 的判斷邏輯:從f開始通過proto一層層往上,能否找到Object.prototype (第二層找到了,正確)
d)注意:
所有的引用類型都能匹配到Object這個構造函數,因爲他們的隱式原型都是對象。
這更加能理解數組和函數是一個特殊的對象,也是其構造函數返回的對象跟我們自己寫個構造方法創建一個對象的形式差不多,都是基於Object而來。
官方定義: 在Javascript中,每一個函數實際上都是一個函數對象。
new一個function 實際上是返回一個函數對象。這與其他的對象有很大的不同。其他的類型Array、Object等都會通過new操作符返回一個普通對象。儘管函數本身也是一個對象,但它與普通的對象還是有區別的,因爲它同時也是對象構造器,所有typeof返回 “function”的對象都是函數對象。因而,所有的構造器都是對象,但不是所有的對象都是構造器。
④ 可以修改Object這個類型來讓所有的對象具有一些通用的屬性和方法:
Function是所有函數對象的基礎,而Object則是所有對象(包括函數對象)的基礎,所有對象都由Object繼承而來。在JavaScript中,任何一個對象都是 Object的實例,因此,可以修改Object這個類型來讓所有的對象具有一些通用的屬性和方法,修改Object類型是通過prototype來完成 的:

以下是引用片段:

Object.prototype.getType=function(){ 
       return typeof(this); 
} 
var array1=new Array(); 
function func1(a,b){ 
      return a+b; 
} 
alert(array1.getType()); 
alert(func1.getType()); 

  上面的代碼爲所有的對象添加了getType方法,作用是返回該對象的類型。兩條alert語句分別會顯示“object”和“function”

如何準確判斷一個變量是數組類型

var arr = [];
arr instanceof Array // true
       // instanceof 只能用來判斷引用類型中的Array和 function ,原因見上文
// 另外typeof 是無法判斷是否是數組的,對於引用類型它只能區分function
typeof arr // object

描述new一個對象的執行過程

創建一個空對象,
this指向這個對象,
執行代碼,即對this賦值,
返回this

原型鏈繼承的實例

function Elem(id){
    this.elem = document.getElementById(id);
}
Elem.prototype.html = function (val) {
    var elem = this.elem;
    if(val){
        elem.innerHTML =val;
        return this; //鏈式操作
    }else{
        return elem.innerHTML
    }
}
Elem.prototype.on = function (type, fn) {
    var elem = this.elem;
    elem.addEventListener(type, fn);
    return this;
}
var div1 = new Elem('detail-page');
//console.log(div1.html());
div1.html('<p> hello </p>').on('click',function(){
    alert('clicked');
})

另:在實際工作中也經常會在公用的js文件中給Date寫一個格式化時間的方法,例如Date.prototype.format = function(fmt) {...},然後新創建的Date對象就可以直接調用format方法了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章