本文根据《JavaScript高级程序设计(第四版)》以及本人理解整理而成,如有不当之处,希望指正~
一、概述
ES5.1并没有正式支持面向对象的结构(类和继承),但是巧妙地运用原型式继承可以模拟与类和继承一样的行为;
ES6正式开始支持类和继承,其实际是封装了ES5.1构造函数和原型继承的语法糖。
二、ES5.1中的伪面向对象
(1)工厂模式
- 特点:能够创建多个类似对象,但是没有解决对象标识问题,即新创建的对象实例是什么类型。(无constructor属性)
(2)构造函数模式
-
定义:构造函数是用来创建特定类型的实例对象。
-
像Object、Array、Set、Map等原生构造函数,运行时可以直接创建实例。
-
特性:①:首字母大写 ②:内部使用this对象 ③:使用new操作符
-
可以自定义构造函数,以函数的形式为对象类型定义属性和方法
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayname = function () { console.log(this.name) } } let person1 = new Person("xiaohong", 18, "docotor"); let person2 = new Person("qiushui", 18, "teacher"); console.log(person1); console.log(person2); -
上例中person1和person2分别保存了Person的不同实例,这两个对象实例都有一个constructor属性指向Person,表示实例对象类型是Person。
-
因此与工厂模式相比,构造函数模式能够解决对象标识问题。
-
但是构造函数任然存在问题,问题在于构造函数定义的方法会在每个实例上都创建一遍,造成相同逻辑的重复定义问题,导致不同实例上的函数虽然同名但不相等。
(3)原型模式
-
引入原型对象的概念,是通过构造函数创建实例对象所用到的原型对象,在上面定义的属性和方法可以被对象实例共享。
function Person() {} Person.prototype.name = "xiaohong"; Person.prototype.age = 18; Person.prototype.job = "doctor"; Person.prototype.sayName = function () { console.log(this.name); } let person1 = new Person(); console.log(person1.sayName()) //xiaohong let person2 = new Person(); console.log(person2.sayName()) //xiaohong -
以上代码中,构造函数里没有属性和方法,将所有属性和方法都直接添加到了Person的prototype属性上,从而实现各个实例对象共享。
-
构造函数的prototype属性指向原型对象;原型对象中的constructor属性指向构造函数;实例中的[[Prototype]]指针被赋值为构造函数的原型对象。
-
Person.prototype.isPrototypeOf(person1) //true
-
Object.getPrototypeOf() //返回实例对象中内部特性[[Prototype]]指针的值
-
Object.setPrototypeOf() //可以向[[Prototype]]指针写入一个新值,不过该方法可能会严重影响代码性能。
-
实例中的同名属性会覆盖原型对象上的同名属性,调用时,先从实例对象查找属性或方法,没有再从原型对象上查找。
-
hasOwnProperty( )作用是确定某个属性或者方法是在实例上还是在原型对象上
三、ES5.1中的伪面向对象继承
-
原型链继承,问题在于当属性值定义在原型对象中时,属性值在各个实例间共享,会造成“属性值污染”。
-
盗用构造函数是指利用call( )、apply( )将执行上下文改为当前对象,问题在于需要在构造函数中定义方法,从而造成相同逻辑的重复定义问题。
-
组合继承: 弥补了原型链和盗用函数的不足,如下例所示:
function Father(name) { this.name = name; this.colors = ["red", "yellow", "blue"] }; Father.prototype.sayName = function () { console.log(this.name); }; function Son(name, age) { Father.call(this, name); //盗用构造函数继承属性 this.age = age; } Son.prototype = new Father(); //利用原型链继承方法 Son.prototype.sayAge = function () { console.log(this.age); }
四、ES6中的面向对象
ES6中主要利用类进行进行对象创建,其中需要借助constructor构造函数器。
class Person { constructor(name, age, job) { this.name = name; this.age = age; this.job = job; } sayName() { console.log(this.name); } } let person1 = new Person("xioahong", 18, "docotor"); person1.sayName()
上例中sayName()方法是定义在原型对象上的。
五、ES6中的面向对象继承
(1)extends( )
-
ES6支持单继承,使用extends关键字,可以继承一个类,也可以继承普通的构造函数。
class Vehicle{} class Bus extends Vehicle{} //继承类Vehicle
(2)super( ) super可以调用父类构造函数
-
super()只能在**派生类构造函数(即子类构造函数中)**和静态方法中使用
-
继承中,如果实例化子类输出一个方法,先看子类是否有这个方法,如果有就先执行系类的,如果子类没有,就去查找父类中有没有这个方法,如果有就执行父类的这个方法(就近原则)
-
子类在构造函数中使用super(),必须将super( )放在this之前调用
class Son extends Father { constructor(x, y) { super(x, y) this.x = x; this.y = y; } subtract() { console.log(this.x - this.y); } }; let son = new Son(5, 3); son.subtract() //2 son.sum() //8