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忽略