代码复用之继承
pfan123 opened this issue · 0 comments
代码复用之继承
js继承通常分两类:原型链的继承,实例对象继承(浅拷贝,深拷贝)
原型链的继承
- 1.借用构造函数:
function Parent(name){
this.name= name || "adam",
this.age="24";
}
Parent.prototype.say = function () {
return this.name
}
function Child(name){
Parent.apply(this, arguments)
}
var child = new Child()
缺点
:子类无法继承父类 prototype
共享属性,原型链断裂。
- 2.借用和设置原型:
function Parent(){
this.name = "adam"
}
Parent.prototype.say = function () {
return this.name
}
function Child(){
Parent.call(this)
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
var child = new Child()
缺点
:实现继承原型,同时继承了两个对象的属性,即添加到this的属性以及原型属性。在绝大多数的时候,并不需要这些自身的属性,因为它们很可能是指向一个特定的实例,而不是复用。
- 3.共享原型:
function Parent(){
this.name = "adam"
}
Parent.prototype.say = function () {
return this.name
}
function Child(){
Parent.call(this)
}
Child.prototype = Parent.prototype;
var child = new Child()
优点
: 效率比较高(不用执行和建立父类Parent的实例了),比较省内存。
缺点
: 子类Child.prototype和父类Parent.prototype现在指向了同一个对象,那么任何对Child.prototype的修改,都会反映到Parent.prototype,影响作用域。
如这样操作Child.prototype.constructor = Child; 则 alert(Parent.prototype.constructor); // Child
- 4.利用空对象作为中介:
function Parent(name){
this.name=name || "gaolu",
this.age="24";
}
Parent.prototype.sayName=function(){
return this.name;
}
function Child(name){
this.sex = “male”,
Parent.apply(this, arguments)
}
function inhert(C,P){
var F=function(){}; //F是空对象,所以几乎不占内存
F.protototype = P.prototype;
C.prototype = new F();
C.prototype.constructor = C;
C.uber = P;
}
inhert(Child, Parent)
var child = new Child()
此种方式最优,也被大多数框架库所采用,节省内存效率较高。
- 5.利用 Object.create 实现继承
//通过Object.create()继承
function Parent(){
this.name = "adam"
}
Parent.prototype.say = function () {
return this.name
}
function Child (name){
Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
console.log(new Child())
- 6.利用 ES6 中 extends 实现继承
使用 extends 实现继承,更简单,不会破坏 instanceof 运算的危险。
class Queue{
consturctor(contents = []) {
this._queue = [...contents]
}
pop() {
const value = this._queue[0];
this._queue.splice(0, i);
return value;
}
}
//继承
class PeekableQueue extends Queue{
constructor(...args) {
super(...args);
}
peek() {
return this._queue[0];
}
}
实例对象继承(非构造函数的继承)
- 浅拷贝
简单的把父对象的属性,全部拷贝给子对象,也能实现继承
function extend(p){
var c = {}
for (var i in p){
c[i] = p[i];
}
c.uber = p;
return c;
}
- 深拷贝
如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。所谓"深拷贝",就是能够实现真正意义上的数组和对象的拷贝。它的实现只要递归调用"浅拷贝"就行了。
function deepExtend(p, c){
var c = c || {};
for(var i in p){
if(typeof p[i] === 'object'){
c[i] = (p[i].constructor === Array) ? [] : {};
//c[i] = (Object.prototype.toString.call(p[i]) === '[object Array]') ? [] : {};
deepExtend(p[i], c[i])
}else{
c[i] = p[i]
}
}
c.uber = p
return c;
}
引伸数组的常用方法拷贝
- 1.slice
arrayObject.slice(start,end)
返回一个新的数组
,不影响父数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。
parent = [1,2,3,4]
//slice() 与 slice(0),开始参数默认为0
child = parent.slice()
child.push(22)
console.log(parent, child) // [1, 2, 3, 4], [1, 2, 3, 4, 22]
- 2.concat
arrayObject.concat(arrayX,arrayX,......,arrayX)
该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本
。
parent = [1,2,3,4]
child = parent.concat([])
child.push(22)
console.log(parent, child) // [1, 2, 3, 4], [1, 2, 3, 4, 22]
- 3.for in 实现拷贝
ps: 三者效率,slice 与 concat 相当,而for in 最差
- 4.扩展运算符(...)
ES6 中,扩展运算符也能很方便的拷贝数组
const items = [1,2,3,4]
const itemCopy = [...items]
注意 splice 不能实现数组拷贝
arrayObject.splice(index,howmany,item1,.....,itemX)
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目,该方法会改变原始数组
。
//使用splice()
var x = [14, 3, 77]
var y = x.splice(1, 2)
console.log(x) // [14]
console.log(y) // [3, 77]
//使用slice()
var x = [14, 3, 77];
var y = x.slice(1, 3);
console.log(x); // [14, 3, 77]
console.log(y); // [3,77]
由此,也发现 splice
与 slice
区别,splice会改变原生数组,而slice返回一个新的数组。
参考: