let arr1 = [1, 2, 3] let arr2 = [4, 5, 6] arr1.concat(arr2) // [1, 2, 3, 4, 5, 6]
首先咱們要清楚 數組也是對象,並且是Array對象的實例 console.log(arr1 instanceof Array) // true 也就是說,下面兩種形式是徹底等價的 let arr1 = [1, 2, 3] let arr2 = new Array(1, 2, 3) 只不過[1,2,3]是一種字面量的寫法
ok,在深刻淺出面向對象和原型【概念篇2】文章裏,咱們提到過segmentfault
類會有一個prototype屬性,而這個類的實例能夠經過__proto__屬性訪問類的prototype屬性
既然arr1是Array對象的實例,那麼arr1天然能夠經過其__proto__屬性訪問到其類Array的屬性
只要Array的prototype裏有concat()這個方法,那麼arr1天然能用數組
console.log(arr1.__proto__ === Array.prototype) // true
按照第一個問題的解決思路,valueOf應該是Array的prototype屬性裏的 可是在Array的prototype屬性裏並無找到valueOf方法 可是咱們又看到了Array的prototype里居然也有__proto__屬性,而且指向Object,在這裏咱們找到了valueOf
如今咱們須要知道的是Array是Object的實例this
console.log(Array instanceof Object) // true
好了,如今咱們知道了 1.arr1 是 Array 的實例 2.Array 是 Object 的實例 當arr1找不到valueOf時,會經過其自身的__proto__屬性去找Array的prototype,看看裏面有沒有 若是Array的prototype裏沒有的話,接下來會經過Array的prototype裏的__proto__屬性去找Object的prototype,看看裏面有沒有 而反觀arr1尋找concat時,由於直接就在Array的prototype裏找到了,因此不會再去經過__proto__尋找Object的prototype裏有沒有
咱們把這個過程抽象化表達
當一個實例調用一個方法時spa
這個不斷經過__proto__屬性和prototype屬性尋找的過程,叫作原型鏈prototype
繼承是指一個對象直接使用另外一對象的屬性和方法
繼承的目的其實就是省代碼,什麼意思呢? 假設你如今是js設計師,你已經設計了Object對象 如今你須要基於Object對象再設計一個Array對象 而且Array對象能夠直接使用Object對象的屬性和方法 這個時候你確定但願可以經過一種方式能直接讓Array對象使用Object對象的屬性和方法 而不是把Object對象的屬性和方法從新寫到Array裏去,這樣作太累了 而這種方式就叫繼承
由繼承的定義,咱們可知,實現繼承必須知足兩個條件 1.獲得一個類的屬性 2.獲得一個類的方法
// 第一個類 function People(name, age) { this.name = name this.age = age } People.prototype = { printName: function () { console.log(this.name) } } // 第二個類 function Male(sex) { this.sex = sex } Male.prototype = { printSex: function () { console.log(this.sex) } }
function Male(sex, name, age) { // 注意Male的實例在這裏就是this // 因此咱們要讓this獲取到People的屬性便可 // 實現方式——讓People的this被替換成Male的this,且讓People被直接調用 People.bind(this)(name, age) this.sex = sex } console.log(new Male('man', 'bruce', '16')) // {name: "bruce", age: "16", sex: "man"}
Object.create()設計
// 例子 let a = Object.create({'zxz': 1}) console.log(a.__proto__) // {zxz: 1} // Object.create() 方法會返回一個對象,這個對象擁有被指定的原型 // 且這個對象可經過自身的__proto__屬性訪問這個原型
Male.prototype = Object.create(People.prototype) // 這一步結束後,Male的實例就能夠經過原型鏈擁有People的方法 // 接着,咱們才能對Male本身要新增的方法進行添加,不然會被覆蓋掉 Male.prototype.printSex = function () { console.log(this.sex) } console.log((new Male('man', 'bruce', '16')).__proto__.__proto__ === People.prototype)
咱們都知道constructor屬性指向其類 當咱們完成第三步後 須要注意的是 console.log(Male.prototype.constructor === People.prototype.constructor) // true 由於執行了Object.create()的緣由,此時Male原型上的constructor被指定成People了 如今須要咱們手動賦值將其更改 Male.prototype.constructor = Male
function People(name, age) { this.name = name this.age = age } People.prototype = { printName: function () { console.log(this.name) } } function Male(sex, name, age) { People.bind(this)(name, age) this.sex = sex } Male.prototype = Object.create(People.prototype) Male.prototype.constructor = Male Male.prototype.printSex = function () { console.log(this.sex) }
function inherit(fatherObject, childObject) { let _prototype = Object.create(fatherObject.prototype); _prototype.constructor = childObject; childObject.prototype = _prototype; }
function inherit(fatherObject, childObject) { let _prototype = Object.create(fatherObject.prototype); _prototype.constructor = childObject; childObject.prototype = _prototype; } function People(name, age) { this.name = name this.age = age } People.prototype = { printName: function () { console.log(this.name) } } function Male(sex, name, age) { People.bind(this)(name, age) this.sex = sex } inherit(People, Male) Male.prototype.printSex = function () { console.log(this.sex) } let bruce = new Male('man', 'bruce', 16)
function People(name, age) { this.name = name this.age = age } People.prototype.sayName = function () { console.log(this.name) } function Student(name, age, score) { People.call(this, name, age) this.score = score } function create(prototypeObj) { let empty = function () {} empty.prototype = prototypeObj return new empty() // return值以下 // { // __proto__:prototypeObj // } } Student.prototype = create(People.prototype) Student.prototype.work = function () { console.log('work') }
當Male的實例繼承了Male的方法和People的屬性和方法後,如何分辨某個屬性或方法是本身的仍是經過原型鏈繼承來的呢? 這就要用到 hasOwnProperty 該方法能夠判斷一個對象是否包含自定義屬性而不是原型鏈上的屬性 而且會忽略掉那些從原型鏈上繼承到的屬性 console.log(bruce.hasOwnProperty('age')) // true console.log(bruce.hasOwnProperty('printSex')) // false 由於printSex方法是bruce實例經過原型鏈從Male類的原型上獲取的,所以會被hasOwnProperty忽略