类的原型对象的做用就是为类的原型添加公有属性和公有方法,但类不能直接访问这些属性和方法,必须经过原型prototype来访问。而咱们实例化一个父类的时候,新建立的对象复制了父类的构造函数内的属性与方法,而且将原型__proto__指向了父类的原型对象,这样就拥有了父类原型对象上的属性和方法,而且这个新建立的对象可直接访问到父类原型对象上的属性与方法,一样也能够访问从父类构造函数中复制的属性和方法。css
var Parent = function() { this.member = ['father', 'mother']; }; var Child = function() {}; Child.prototype = new Parent(); // test: var child1 = new Child(); var child2 = new Child(); console.log(child1.member); // ["father", "mother"]; console.log(child2.member); // ["father", "mother"]; child1.member.push('uncle'); console.log(child1.member); // ["father", "mother", "uncle"]; console.log(child2.member); // ["father", "mother", "uncle"];
一、因为子类经过其原型prototype对父类实例化,继承了父类,因此说父类中的公有属性要是引用类型,就会在子类中被全部实例共用,所以一个子类的实例更改子类原型从父类构造函数中继承出来的公有属性就会影响到其余子类。html
二、因为子类实现的继承是靠其原型prototype对父类的实例化实现,所以在建立父类的时候,是没法向父类传递参数的,于是实例化父类的时候也没法对父类构造函数内的属性进行初始化。html5
因为call方法能够更改函数的做用域,所以在子类中,对父类调用这个方法就是将子类中的变量在父类中执行一遍,
因为父类中是给this绑定属性的,所以子类天然就继承了父类中的公有属性。web
var Parent = function() { this.member = ['father', 'mother']; this.speak = function() { console.log('Chinese!'); } }; Parent.prototype = { constructor: Parent, say: function() { console.log('Hi!'); } }; var Child = function() { Parent.call(this); }; // test: var child1 = new Child(); var child2 = new Child(); console.log(child1.member); // ["father", "mother"]; console.log(child2.member); // ["father", "mother"]; console.log(child1.speak()); // Chinese! console.log(child1.say()); // Uncaught TypeError: child1.say is not a function child1.member.push('uncle'); console.log(child1.member); // ["father", "mother", "uncle"]; console.log(child2.member); // ["father", "mother"];
因为这种类型的继承没有涉及原型prototype,因此父类的原型方法天然不会被子类继承,而若是要想被子类继承就必需要放在构造函数中,这样建立出来的每一个实例都会单独拥有一份而不能共用,这样就违背了代码复用的原则.缓存
在子类构造函数中执行父类构造函数,在子类原型上实例化父类,融合了类式继承和构造函数继承二者的优势。并过滤了其缺点。函数
var Parent = function() { this.member = ['father', 'mother']; this.speak = function() { console.log('Chinese!'); } }; Parent.prototype = { constructor: Parent, say: function() { console.log('Hi!'); } }; var Child = function() { Parent.call(this); }; Child.prototype = new Parent(); // test: var child1 = new Child(); var child2 = new Child(); console.log(child1.member); // ["father", "mother"]; console.log(child2.member); // ["father", "mother"]; console.log(child1.speak()); // Chinese! console.log(child1.say()); // Hi! child1.member.push('uncle'); console.log(child1.member); // ["father", "mother", "uncle"]; console.log(child2.member); // ["father", "mother"];
在子类构造函数中执行了一遍父类构造函数,在实现子类原型的类式继承时又调用了一遍父类构造函数,所以调用了两遍构造函数。this
对类式继承的一个封装prototype
// 声明一个过渡对象继承父对象, 并返回过渡对象的实例 function inheritObject(o) { function F() {}; F.prototype = o; return new F(); }; var book = { name: 'web', type: ['html', 'css'] }; // test: var html5Book = inheritObject(book); html5Book.name = 'html5Book'; html5Book.type.push('html5'); var jsBook = inheritObject(book); jsBook.name = 'jsBook'; jsBook.type.push('js'); console.log(html5Book.name); console.log(html5Book.type); // ["html", "css", "html5", "js"]; console.log(jsBook.name); console.log(jsBook.type); // ["html", "css", "html5", "js"];
与类式继承同样, 父类对象中的值类型被复制, 引用类型的属性被共用.code
对原型继承的第二次封装, 而且在第二次封装过程当中对继承的对象进行扩展,
这样新建立的对象不单单有父类中的属性和方法, 并且还添加新的属性和方法.
寄生式继承依托于原型继承模式同时也是为了寄生组合式继承模式的实现。htm
function inheritObject(o) { // 声明一个过渡对象继承父对象, 并返回过渡对象的实例 function F() {}; F.prototype = o; return new F(); } var book = { name: 'web', type: ['html', 'css'] }; function createBook(obj) { var o = new inheritObject(obj); o.getName = function() { console.log('webBook'); }; return o; } var newBook = createBook(book); console.log(newBook.name); // web console.log(newBook.type); // ['html', 'css'] console.log( newBook.getName() ); // webBook
对子类赋予父类原型的一个引用.即须要父类的原型对象的一个副本, 而这副本能够经过原型继承获得,
但由于这样直接赋值给子类会形成父类原型对象复制获得的复制对象p中的constructor指向不是子类对象,
所以须要对复制对象p作一次加强, 修复其constructor属性指向不正确的问题, 最后将获得的复制对象p赋值给子类的原型, 这样子类的原型就继承了父类的原型而且没有执行父类的构造函数.
function inheritObject(o) { // 声明一个过渡对象继承父对象, 并返回过渡对象的实例 function F() {}; F.prototype = o; return new F(); } function inheritPrototype(subClass, superClass) { // 复制一份父类的原型副本保存在变量中 var p = inheritObject(superClass.prototype); // 修正由于重写子类原型致使子类的constructor属性被修改 p.constructor = subClass; // 设置子类原型 subClass.prototype = p; } var Parent = function(language) { this.language = language; this.member = ['father', 'mother']; this.speak = function() { console.log(this.language); } }; Parent.prototype = { constructor: Parent, say: function() { console.log('Hi!'); } }; var Child = function(language, name) { Parent.call(this, language); this.name = name; }; inheritPrototype(Child, Parent); // test: var child1 = new Child('English', 'xiaoming'); var child2 = new Child('japanese', 'xiaoli'); child1.member.push('uncle'); console.log( child1.speak() ); // English console.log( child1.say() ); // Hi! console.log( child1.member ); // ["father", "mother", "uncle"] console.log( child2.speak() ); // English console.log( child2.say() ); // Hi! console.log( child2.member ); // ["father", "mother"] Child.prototype.getName = function() { console.log('child~'); }; var child3 = new Child(); console.log( child3.getName() ); // child~ console.log( child3.say() ); // Hi~ Child.prototype = { getMember: function() { console.log(this.member); } }; var child4 = new Child(); console.log( child4.getMember() ); // ["father", "mother"] console.log( child4.say() ); // Uncaught TypeError: child3.say is not a function
子类再想添加方法必须经过prototype.对象, 经过点语法的形式一个一个添加方法, 不然直接赋予对象就会覆盖从父类原型继承的对象.
单继承 属性复制
var extend = function(target, source) { // 遍历源对象的属性 for(var property in source) { // 将源对象中的属性复制到目标对象中 target[property] = source[property]; } //返回目标对象 return target; };
多继承 属性复制
var mix = function() { var i = 1, // 从第二个参数起为被继承的对象 len = arguments.length, // 获取参数长度 target = arguments[0], // 第一个对象为目标对象 arg; // 缓存参数对象 for(; i < len; i++) { // 缓存当前对象 arg = arguments[i]; // 遍历被继承对象中的属性 for(var property in arg) { // 将被继承对象中的属性复制到目标对象中 target[property] = arg[property]; } } // 返回目标对象 return target; };