1. 程式人生 > >js---原型,原型鏈

js---原型,原型鏈

1.原型---Person.prototype,在函式Person定義的時候就產生了,是所有由Person建構函式構造出的物件的祖先。祖先意味著子代可以繼承他的屬性。而這個Person.prototype等待著new時將自己放到this的__proto__中。

function Person(){
  //var this={__proto__:Person.prototype}
  ....//中間的程式碼
  // return this;
}
console.log(Person.prototype);//{constructor:Person(){},__proto__:Object}

2.原型鏈---就是在new Person()的時候,產生的this的同時,系統會給this配置一個屬性__proto__,值是當前建構函式的原型即Person.prototype,通過原型的__proto__可以構成原型鏈。Person.prototype的__proto__指向的是Object,就構成了有兩個原型的原型鏈,也就是person在Person.prototype找不到,可以到Object.prototype裡找。

一切原型鏈的終端都是Object:

例如:

function Person(){
  //var this={__proto__:Person.prototype}
  // return this;
}
var person = new Person();
console.log(person.toString());//[object Object]  
//事實上在Person.prototype裡沒有這個toString方法,可以person依然可以呼叫,因為這個方法在Object.prototype上

3.原型系統預設會配屬性:

constructor---指向當前原型是誰(哪個建構函式)的原型

__proto__----指向原型鏈中上一級的原型物件,可以被修改,修改後順著__proto__找

例如:

function Person(){}
Person.prototype.name='ren';
function Student(){
  this.name='xuesheng'
}
var person = new Person();
Person.prototype = new Student();
console.log(person.name);//ren  為啥這裡改了沒用?因為你先new出來person,此時person的__proto__已經被Person.prototype賦值了。
//就是說__proto__裡存的是Person.prototype的地址,你後來又給Person.prototype這個索引名字換了一個地址當然沒作用
//你要是直接給person.__proto__換物件就會變,詳情可以看原始型和引用型那章部落格
var person1=new Person();
console.log(person1.name);//xuesheng 原型變成了student再new的
Person.prototype.name='lin';
console.log(person.name);//'ren' 修改新原型(student)的name,原先的原型不會改變
var person2 = new Person();
console.log(person2.name);//'lin' 此原型已經變了,再new就是遵循新的原型,而新的原型的name剛剛被改成了'lin'


4.不是所有物件都有原型的,比如由Object.create(null)出的物件,是沒有原型的。

Object.create(原型)就是建立以什麼為原型的物件。

例如:

var obj = {name:'Lin',age:12,sex:'female'};
var obj1 = Object.create(obj);
console.log(obj1,obj1.name);//{__proto__:{age:12,name:'Lin',sex:'female'}}  "Lin" 本身沒有但是通過原型鏈找到父級有name屬性
var obj2 = Object.create(null);
console.log(obj2);//{} 什麼屬性都沒有的空物件
obj2.__proto__= obj //如果人為加__proto__是沒有用的,系統預設產生的屬性__proto__可以改,但是沒有就是沒有,人為加了也不會產生原型鏈的效果
console.log(obj2.name);//undefined
Object.create(原型,是否為可配置);
例子---啥叫可配置:凡是經過var宣告出來的都是不可配置的,不能delete的
//在全域性宣告變數,相當於window.num = 123
var num = 123;
delete num;
console.log(num);//123 並沒刪掉--不可配置屬性
var obj={name:'lin'}
delete obj.name;
console.log(obj.name);//undefined 刪掉了--屬於可配置的屬性

5.賦值,正常來說子代只能獲得父代的值,不能新增修改刪除

但是,如果父代上時引用型別的值,可以通過子代改

function Father(){
    this.fortune={
        card1:'vasas'
    }
    this.name = 'heel';
    this.hel= 'haha'
}
function Son(){}
var father = new Father();
Son.prototype = father;
var son = new Son();
console.log(son.fortune.card1,son.name);//'vasas' 'heel'取到原型上的值
son.fortune.card1=2;
son.name=1;
console.log(father.fortune.card1,father.name,son.name);//2 'heel' 1 原型上原始值沒有被修改,引用型別值被修改,自己身上添加了新屬性
//想修改原型中的原始值,唯一方法就要在原型上操作
Son.prototype.hel='shen';
Son.prototype.name='haha';
console.log(father.hel,son.hel,father.name,son.name);//shen shen haha 1自己身上有就不會去原型身上找

6.原型鏈上常有名字相同的函式名,在執行的時候採取就近原則,找到就停止尋找,這是一種函式重寫的方式。當然如果想要用某個原型上的方法,而不是預設的就近原則,可以用call/apply

var num=123,arr=[1,2,3],bol=true,str='12';
console.log(num.toString(),arr.toString(),bol.toString(),str.toString());//'123' '1,2,3' 'true' '12'
//還記得我們前面說的object.prototype上面的toString()出來的結果是類似於[Object XXX]的形式
//那麼這幾個為什麼呼叫toString()之後都是字串,證明Number.prototype,Array.prototype,Boolean.prototype,String.prototype上有toString函式的重寫
//當然,如果你需要的時候也可以在原型鏈上採用重寫的方式

//另外 document.write({})----[Object Object]說明是系統隱式呼叫了toString方法轉換再輸出
//所以undefined null 自己構造的沒有原型的物件 都不能呼叫toSting() 也不能用document.write()方式列印