js 和 Java、C++ 等传统面向对象的编程语言不同,它是没有类(class,ES6 中的class也只不过是语法糖,并非真正意义上的类)的概念,而在 js 中,一切皆是对象(object)。在基于类的传统面向对象的编程语言中,对象由类实例化而来,实例化的过程中,类的属性和方法会被拷贝到这个对象中;对象的继承实际上是类的继承,在定义子类继承于父类时,子类会将父类的属性和方法拷贝到自身当中。这类语言中,对象创建和继承行为都是通过拷贝完成的。但在 js 中,对象的创建、对象的继承(更好的叫法是对象的代理,因为它并不是传统意义上的继承)是不存在拷贝行为的。因此类、继承这些特点都不属于 js
prototype 、 __proto__ 以及 constructor
__proto__
和constructor
是每个对象都有的属性,而prototype
是函数对象独有的属性
__proto__
属性指向它们的原型对象(也可以理解为父对象),作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__
属性所指向的那个对象(父对象)里找,如此反复,直到__proto__
属性的值为 null,然后返回 undefined,__proto__
的终点是 null。通过__proto__
属性将对象连接起来的这条链就是 原型链
prototype
属性指向这个函数的原型对象,也就是这个函数创建的实例对象的原型对象,作用就是让该函数所实例化的对象们都可以找到公用的属性和方法。任何函数在创建的时候,其实会默认同时创建该函数的原型对象
constructor
属性指向该对象的构造函数,每个对象都有构造函数(本身拥有或继承而来),Function对象比较特殊,它的构造函数就是自己,所以constructor
属性的终点就是Function函数
function People(){
...
}
var people = new People();
console.log(people.__proto__ === People.prototype)//true
console.log(people.constructor === People.prototype.constructor)//true
关系图如下:
graph LR
prototype(原型对象)
constructorFunc(构造函数)
object(实例对象)
constructorFunc --new--> object
constructorFunc --prototype--> prototype
prototype --constructor--> constructorFunc
object -.constructor.-> constructorFunc
object --__proto__--> prototype
js 实现类
在 ES6 之前 js 还没有 class 关键字,可以使用一下几种方式模拟声明类:
1、工厂模式
function createPerson(name){
var person = new Object();
person.name = name;
return person;
}
var person = createPerson('Leo')
2、构造函数法:
function Person(name){
this.name = name;//this 代表新创建的实例对象
}
var person = new Person('Leo')
在 ES6 新增 class 关键字声明类(其实只是语法糖):
class People{
name = 'default';
constructor(name){
this.name = name;
}
}
js 实现继承
背景
js 是由 Brendan Eich 负责开发的,当时面向对象很兴盛,Brendan Eich 无疑也受到了影响,js 中一切皆为对象,所以必须有一种机制,将所有对象联系起来。但是,Brendan Eich 没引入"类"的概念,因为一旦有了"类",js 就是一种完整的面向对象编程语言了,过于正式了。想到 C++ 和 Java 使用 new 命令时,都会调用"类"的构造函数 constructor。他就做了一个简化的设计,在 js 中,new 命令后面跟的不是类,而是构造函数
构造函数继承
说明:直接利用 call 或者 apply 方法将父类构造函数的 this 绑定为子类构造函数的 this 就可以
缺点:无法继承父类原型链上的属性与方法
function Parent(){
this.name = "parent's name"
}
Parent.prototype.say = function(){
console.log(this.name)
}
function Child(){
Parent.call(this) //构造函数继承核心代码
}
let child = new Child()
console.log(child.name) // parent
console.log(child.say()) // child.say is not a function
原型继承
说明:将子类的原型挂载到父类上
缺点:子类 new 出来的实例,父类的属性没有隔离,会相互影响
function Parent() {
this.arr = [1, 2, 3]
}
function Child() {
}
Child.prototype = new Parent() //原型继承核心代码
let child1 = new Child()
let child2 = new Child()
child1.arr.push(4)
console.log(child1.arr, child2.arr) // [1, 2, 3, 4] [1, 2, 3, 4]
组合继承
说明:组合上面的构造函数与原型继承的功能
缺点:call() 方法已经拿到父类所有的属性 ,后面再使用原型时也会有父类所有属性
function Parent() {
this.name = 'parent'
}
Parent.prototype.say = function () {
console.log(this.name)
}
function Child() {
Parent.call(this)
}
Child.prototype = new Parent()
let child = new Child();
child:
Child {name: "parent"}
name: "parent"
__proto__: Parent
name: "parent" // 重复的属性
__proto__:
say: ƒ ()
constructor: ƒ Parent()
__proto__: Object
寄生式组合继承
说明:解决组合继承重复属性的问题,直接将子类的原型等于父类的原型,或者是用 Object.create 继承原型但不执行父类构造函数(目前最完美的继承实现方式)
function Parent() {
this.name = 'parent'
}
Parent.prototype.say = function () {
console.log(this.name)
}
function Child() {
Parent.call(this)
this.type = 'child'
}
Child.prototype = Parent.prototype
Child.prototype.constructor = Child
//修复重写子类原型导致子类constructor属性被修改
Class 继承
说明:ES6 新增,class 是一个语法糖,基于寄生组合继承来实现
class Parent{
constructor(){
this.name = 'parent'
this.arr = [1, 2, 3]
}
say(){
console.log(this.name)
}
}
class Child extends Parent{
constructor(){
super() // 通过 super() 调用父类构造函数
}
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!