js 原型、继承整理

JavaScript 常被描述为一种基于原型的语言 (prototype-basedanguage)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。
原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。
准确地说,这些属性和方法定义在Object的构造器函数(constructorfunctions)之上的prototype属性上,而非对象实例本身。通过原型这种机制,JavaScript 中的对象从其他对象继承功能特性

  • 在javascript中,每个函数都有一个特殊的属性叫作原型(prototype)
  • 对象的原型可以通过Object.getPrototypeOf(obj)或者已被弃用的__proto__属性获得
  • prototype是函数才有的属性,__proto__属性是对象的属性,大多数情况下,__proto__ 可以理解为“构造器的原型”,即__proto__===constructor.prototype,但是通过 Object.create()创建的对象有可能不是.

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

function Constructor(){}
o = new Constructor();
// 等价于:
o = Object.create(Constructor.prototype);
let obj1 = { a: 1 };
let obj2 = Object.create(obj1);
 console.log('obj2.constructor',obj2.constructor)
 console.log("obj2.__proto__:",obj2.__proto__); //Object {a: 1}

Object.getPrototypeOf(obj)
在ES5中,如果传递给方法的参数不是对象,则会抛出TypeError异常。
在ES6中,如果传递给方法的参数不是对象,则会强制类型转换为对象。

原型继承、构造函数继承、组合继承

在这里插入图片描述

ES5实现继承

  • 原型链(无法设置参数)
  • 借用构造函数
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承
    ES6实现继承
  • es6继承

原型链继承(无法设置参数)
让新实例的原型等于父类的实例。
优点:能通过instanceOf和isPrototypeOf的检测

function Person() {}
function Man(name,nationality) {
    this.name = name 
    this.nationality = nationality
}
Man.prototype = new Person()
console.log(Man.prototype)
var man = new Man('zhangsan','English')
console.log(man.name)
VM219:7 Person {}
VM219:9 zhangsan
function Person (name,age) {
	this.name = name;
	this.age = age;
	this.talk = function() {
		console.log('person talk')
	}
	function say() {
		console.log(this.name + 'is' + this.age)
	}
}
Person.prototype.weight = '50'
Person.prototype.sayWeight = function() {
	console.log('i am' + this.weight)
}
function Femal() {}
Femal.prototype = new Person()
// console.log(Femal)
var a = new Femal
console.log(a)
// Femal {}
//  __proto__: Person
//    age: undefined
//    name: undefined
//    talk: ƒ ()
a.talk()  // person talk
// 可以继承父类原型上的属性
console.log(a.weight) // 50

借用构造函数
用.call()和.apply()将父类构造函数引入子类函数
优点:可以在子类给父类传递参数
缺点:无法继承父类原型上的属性和方法

function Person (name,age) {
	this.name = name;
	this.age = age;
	this.talk = function() {
		console.log('person talk')
	}
}
Person.prototype.weight = '50'
Person.prototype.sayWeight = function() {
	console.log('i am' + this.weight)
}
// 使用构造函数
function Student(sex,name,age) {
	Person.call(this, name, age)
	this.sex = sex
}
var s1 = new Student('girl', 'wangwu', 8)
console.log(s1) // Student {name: "wangwu", age: 8, sex: "girl", talk: ƒ}
console.log(s1.weight) // undefined

组合继承
结合原型链继承和使用构造函数继承

缺点: 调用了两次构造函数,耗内存

function Person (name,age) {
	this.name = name;
	this.age = age;
	this.talk = function() {
		console.log('person talk')
	}
}
Person.prototype.weight = '50'
function Child(sex,name,age) {
	this.sex = sex
	Person.call(this,name,age)
}
Child.prototype = new Person()

// console.log(Child)

var c1 = new Child('boy', 'lihua', 2)
console.log(c1) // Child {sex: "boy", name: "lihua", age: 2, talk: ƒ}
console.log(c1.weight) // 50
	

原型式继承
必须有一个对象可以作为另一个对象的基础
使用场景:一个对象与另外一个对象保持相似,类似于Object.create 方法; 不需要构造函数。

  1. 定义一个构造函数
  2. 把传入的对象赋给构造函数的原型
  3. 返回构造函数的一个实例
var cat = {
	eye: true,
	eat: function() {
		console.log('i can eat')
	}
}
function Dag(name) {
	function Animal() {
		this.name = name
	}
	Animal.prototype = cat
	return new Animal()
}

var d1 = Dag('大黄')
console.log(d1) // Animal {name: "大黄"}
console.log(d1.name) // 大黄
d1.eat() // i can eat

略作处理

var  chicken = {
	eye: true,
	fly: function() {
		console.log('I can fly')
	}
}
function duck(obj, props) {
	function bird() {
		for(var prop in props) {
			this[prop] = props[prop]
		}
	}
	bird.prototype = obj
	return new bird()
}
var bjduck = duck(chicken, {
	name: 'roast duck',
	cooking: function() {
		console.log('北京烤鸭')
	}
})
bjduck.fly() // I can fly

寄生式继承
把原型式继承再次封装,然后在对象上扩展新的方法,再把新对象返回

var car = {
	wheel: 4,
	run: function() {
		console.log('我可以跑')
	}
}
function clone(obj) {
	function cloneObj() {}
    cloneObj.prototype = obj;
    return new cloneObj();
}
function createAnother(obj, props) {
	var copy = clone(obj)
	for(var prop in props) {
		copy[prop] = props[prop]
	}
	copy.method = function() {
		console.log('我自己的方法')
	}
	return copy
}

var anotherCar = createAnother(car,{
	color: 'red'
})
console.log(anotherCar) 
/* 输出结果
cloneObj {color: "red", method: ƒ}
color: "red"
method: ƒ ()
__proto__:
	wheel: 4
	run: ƒ () 
*/

寄生组合式继承,
通过借用构造函数来继承属性, 在原型上添加共用的方法, 通过寄生式实现继承.

function SuperType(name){ 
	this.name = name; 
} 
function inherit(subType, superType){ 
	//创建对象 
	var prototype = Object.create(superType.prototype);
	 //增强对象,弥补因重写原型而失去的默认的constructor属性
	prototype.constructor = subType;
	//指定对象 
	subType.prototype = prototype; 
	// SubType.prototype = new SuperType();
} 
// 组合
function SubType(name, age){
	SuperType.call(this, name); 
	this.age = age; 
} 
inherit(SubType, SuperType);

继承时修复实例的意义:
为了让prototype的构造函数重新指回这个类本身,否则的话它会变成之前继承的那个类的构造函数。

es6 继承

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  // 方法之间不需要逗号分隔,加了会报错。
  // 类的内部所有定义的方法,都是不可枚举的
  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
// 类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面
Object.assign(Point.prototype, {
  toValue(){}
});
// 类必须使用new调用
var point = new Point(2, 3);
// Class之间可以通过extends关键字实现继承
class ColorPoint extends Point {
  constructor(x, y, color) {
  //因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}

Class不存在变量提升(hoist)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值