天天看點

JS-原型鍊

原型prototype

    • 原型的定義
          • 注意
    • 原型的用途
    • 原型的特點
    • 原型鍊
        • Function函數
        • Object函數
        • 函數對象關系
        • 原型鍊機制

原型的定義

  • 在JavaScript中,任何一個函數,都有一個prototype屬性,指向一個對象。
  • 輸出了一個函數的prototype屬性,你會發現是一個空對象。輸出這個prototype的類型,發現是object類型
  • prototype就是英語“原型”的意思。每個函數都有原型,原型是一個對象
function fun(){
	alert("Hi");
}

console.log(fun.prototype);				// Object{}
console.log(typeof fun.prototype);		// Object
           
// 構造函數,構造函數裡面沒有任何語句,也就是說,
	// 這個構造函數在執行的時候,不會給建立出來的對象添加任何屬性。
    function Person() {

    }
    // 構造函數的原型,我們更改了構造函數的原型,為一個新的對象:
    Person.prototype = {
      name: "蔡徐坤",
      sex: "男",
      age: 18
    }

    // 當一個對象被new出來的時候,不僅僅執行了構造函數裡面的語句,
    // 也會把這個函數__proto__指向構造函數的Person。
    let obj= new Person();

    console.log(obj.__proto__);			// {name: "蔡徐坤", sex: "男", age: 18}
    console.log(obj.__proto__ == Person.prototype);		// true

    //當我們試圖通路name、sex、age屬性的時候,身上沒有
    // 那麼就去查找原型,原型身有,就當做了自己的屬性傳回了
    console.log(obj.name);		// 蔡徐坤
    console.log(obj.sex);		// 男
    console.log(obj.age);		// 18
           

prototype一定是函數的屬性!當這個函數是一個構造函數的時候,那麼它new出來的對象,将以它的原型那個對象為new出來的執行個體的原型對象

JS-原型鍊
注意

任何一個對象,都有__proto__屬性,這個屬性指向自己的原型對象

原型的用途

定義一個方法的時候,如果寫在構造函數裡面:

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.say = function () {
        console.log("你好,我是" + this.name + "我今年" + this.age + "歲了");
    }
}
let obj1 = new Person("菜虛鲲", 12);
let obj2 = new Person("踩墟捆", 11);
obj1.say();			// 你好,我是菜虛鲲我今年12歲了
obj2.say();			// 你好,我是踩墟捆我今年11歲了
           

實際上,這個函數被複制了兩份,一份給了obj1,一份給了obj2, obj1和obj2這兩個執行個體身上有了相同功能的函數,但是這個函數不是同一個函數

那該如何解決這個問題呢, 一句話:所有的屬性要綁在對象身上,而所有的方法定義在對象的原型對象中:

function Person(name, age) {
    // 構造函數裡面,負責定義一些屬性,随着構造函數的執行,這些屬性将綁定到new出來的對象身上
    this.name = name;
    this.age = age;
}
// 把所有的方法,定義在原型對象身上:
Person.prototype.say = function () {
    console.log("你好,我是" + this.name + "我今年" + this.age + "歲了");
}
let obj1 = new Person("菜虛鲲", 12);
let obj2 = new Person("踩墟捆", 11);
obj1.say();			// 你好,我是菜虛鲲我今年12歲了
obj2.say();			// 你好,我是踩墟捆我今年11歲了
           

證明了obj1的say方法和obj1的say方法,是同一個函數 , 記憶體消耗小很多

原型的特點

  • 存儲在prototype中的方法可以被對應構造函數建立出來的所有對象共享
  • prototype中除了可以存儲方法以外, 還可以存儲屬性
  • prototype如果出現了和構造函數中同名的屬性或者方法, 對象在通路的時候, 通路到的是構造函中的資料
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.currentType = "構造函數中的type";
    this.say = function () {
        console.log("構造函數中的say");
    }
}
Person.prototype = {
    currentType: "人",
    say: function () {
        console.log("hello world");
    }
}
let obj1 = new Person("菜虛鲲", 34);
obj1.say();			// 構造函數中的say
console.log(obj1.currentType);	// 構造函數中的type
let obj2 = new Person("踩墟捆", 44);
obj2.say();			// 構造函數中的say
console.log(obj2.currentType);	// 構造函數中的type
           
  • 在給一個對象不存在的屬性設定值的時候, 不會去原型對象中查找, 如果目前對象沒有就會給目前對象新增一個不存在的屬性
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype = {
    constructor: Person,
    currentType: "人",
    say: function () {
        console.log("hi");
    }
}
let obj = new Person("蔡徐坤", 12);

obj.currentType = "新設定的值";

console.log(obj.currentType); // 新設定的值
console.log(obj.__proto__.currentType); // "人"
           

原型鍊

Function函數

  • JavaScript中函數是引用類型(對象類型), 既然是對象, 是以也是通過構造函數建立出來的, "所有函數"都是通過Function構造函數建立出來的對象
  • JavaScript中隻要是"函數"就有prototype屬性
  • Function函數"的prototype屬性指向"Function原型對象"
  • JavaScript中隻要是"原型對象"就有constructor屬性
  • "Function原型對象"的constructor指向它對應的構造函數
  • Person構造函數是Function構造函數的執行個體對象, 是以也有__proto__屬性
  • Person構造函數的__proto__屬性指向"Function原型對象"
    JS-原型鍊
function Person(name, age) {
    this.name = name;
    this.age = age;
}
let obj1 = new Person('踩墟捆', 12);
console.log(Function);        // ƒ Function() { [native code] }
console.log(Function.prototype);    // ƒ () { [native code] }
console.log(Function.prototype.constructor);    // ƒ Function() { [native code] }
console.log(Function === Function.prototype.constructor); // true
console.log(Person.__proto__);    // ƒ () { [native code] }
console.log(Person.__proto__ === Function.prototype); // true
           

Object函數

  • Object是一個函數,是系統内置的構造函數,用于創造對象的 , Object.prototype是所有對象的原型鍊終點

    是以,當我們在一個對象上打點調用某個方法的時候,系統會沿着原型鍊去尋找它的定義,一直找到Object.prototype

JS-原型鍊
function Person(name, age) {
    this.name = name;
    this.age = age;
}
let obj1 = new Person('踩墟捆', 19);
console.log(Function.__proto__);      		// ƒ () { [native code] }
console.log(Function.__proto__ === Function.prototype);	 // true
console.log(Object);          				// ƒ Object() { [native code] }
console.log(Object.__proto__);    			// ƒ () { [native code] }
console.log(Object.__proto__ === Function.prototype);	 // true
console.log(Object.prototype);    			// {constructor: ƒ, 
__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, 
__lookupGetter__: ƒ, …}
console.log(Object.prototype.constructor);  // ƒ Object() { [native code] }
console.log(Object.prototype.constructor === Object);	 // true
console.log(Object.prototype.__proto__); 	// null
           

函數對象關系

  • 所有的構造函數都有一個prototype屬性, 所有prototype屬性都指向自己的原型對象
  • 所有的原型對象都有一個constructor屬性, 所有constructor屬性都指向自己的構造函數
  • 所有函數都是Function構造函數的執行個體對象(Function函數是所有函數的祖先函數)
  • 所有函數都是對象, 包括Function構造函數
  • 所有對象都有__proto__屬性
  • 普通對象的__proto__屬性指向建立它的那個構造函數對應的"原型對象"
  • 所有對象的__proto__屬性最終都會指向"Object原型對象"
  • "Object原型對象"的__proto__屬性指向NULL
    JS-原型鍊
function Person(name, age) {
    this.name = name;
    this.age = age;
}
let obj1 = new Person('踩墟捆', 12);
console.log(Function.prototype.__proto__);      // Object
console.log(Person.prototype.__proto__);        // Object
console.log(Function.prototype.__proto__ === Person.prototype.__proto__);   // true
console.log(Function.prototype.__proto__ === Object.prototype);  			//true
console.log(Person.prototype.__proto__ === Object.prototype);   			// true
           

原型鍊機制

  • 對象中__proto__組成的鍊條我們稱之為原型鍊
  • 對象在查找屬性和方法的時候, 會先在目前對象查找
  • 如果目前對象中找不到想要的, 會依次去上一級原型對象中查找 , 如果找到Object原型對象都沒有找到, 就會報錯
JS-原型鍊
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.currentType = "構造函數中的type";
}
Person.prototype = {
    // 注意點: 為了不破壞原有的關系, 在給prototype指派的時候
    // 需要在自定義的對象中手動的添加constructor屬性, 手動的指定需要指向誰
    constructor: Person,
    // 構造函數中的屬性或方法會替換原型對象中的屬性或方法
    currentType: "人",
    say: function () {
       console.log("Hi");
    }
}
let obj1 = new Person("lnj", 34);

console.log(obj1.currentType);			// 構造函數中的type
console.log(Person.prototype.constructor);		// ƒ Person(name, age) {...}
           

繼續閱讀