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
方法; 不需要构造函数。
- 定义一个构造函数
- 把传入的对象赋给构造函数的原型
- 返回构造函数的一个实例
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)