pfan123/Articles

代码复用之继承

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()

//通过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]

由此,也发现 spliceslice 区别,splice会改变原生数组,而slice返回一个新的数组。

参考:

从__proto__和prototype来深入理解JS对象和原型链

Javascript 面向对象编程(一):封装

Javascript面向对象编程(二):构造函数的继承

Javascript面向对象编程(三):非构造函数的继承

Object.create()