正如上篇所提到的,有些人认为JavaScript并非真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象,而后您能够简单地定义哪些类继承哪些类,JavaScript使用了另外一套实现方式,继承的对象函数并非经过复制而来,而是经过原型链继承(一般被称为 原型式继承 —— prototypal inheritance)。ios
一个函数,有三种角色。
当成普通函数,当成构造函数,当成对象es6
function Person (nickname) { var age = 15 //普通函数 标记1 this.nickname = nickname //构造函数 this.sayName = function() { console.log(this.nickname) } console.log(age) 标记2 } Person.nickname = '张三' //对象 var p1 = new Person('李四') //'15' console.log(Person.nickname) //'张三' p1.sayName() //'李四' 标记4
若是标记1处改为 var nickname = 'fyy' 标记2处改为 console.log(nickname)
则后面的输出分别为 'fyy' '张三' 'fyy'axios
实例属性:又称成员就是实例里面的属性babel
var p1 = new Person('fyy',11) p1// {age:11,name:'fyy'} //name和age就是实例属性
原型属性:就是类的原型上的属性
Person.prototype.say = function(){}
ar p1 = new Person('fyy',11)
p1.say // say就是调用的Person的原型属性app
私有属性:就是只能在类的内部访问的方法和属性mvvm
class IncreasingCounter { #count = 0; get value() { console.log('Getting the current value!'); return this.#count; } increment() { this.#count++; } } const counter = new IncreasingCounter(); counter.#count // 报错 counter.#count = 42 // 报错
静态方法:类上的静态方法就表示该方法不会被实例继承,而是直接经过类来调用
Array的isArray就是静态方法,只能经过实例调用由于挂在原型上没有意义,必然是Array的实例才能调用
同理还有String的fromCharCode等等函数
Array.isArray([1,2,3]) // //定义 class Array { static isArray(){//...} constructor(){ //... } //... }
说到继承,首先得明白继承的是什么东西。我的认为继承应该分为成员属性和原型属性的继承。
实例属性是不能共用的属性或者方法,好比身份证。
原型属性是能共用的属性或者方法,好比爱好属性,吃饭方法。this
这个比较简单,实例属性用利用构造函数和call或者applyspa
const Person = function (name) { this.name = name } const Students = function (name) { Person.call(this,name) } const xm = new Students('小明') console.log(xm) //Students {name: "小明"}
这里里面坑有点多,你们听我娓娓道来。
经过原型链实现原型属性继承确定没错,可是咱们设计的时候有个原则 子类须要有本身的原型,父类也必需要有本身的原型,子实例在本身的原型上找不到属性的时候才会到父原型上去找
子类.prototype = 父类.prototype 这样确定不行,虽然能继承父类原型的方法,可是子类的原型和父类的原型是同一个,给子类原型添加方法的时候,至关于给父类的原型也添加了一个方法。so咱们应该有一个缓冲
。prototype
子类实例---->子类原型------->中间对象------->父类原型 //沿着箭头能访问,表现上符合咱们的设计原则
最多见的是使用父类的实例当这个中间对象。
Children.prototype = new Parent()
可是了这么作有个很差的地方。会实例化一次父类。若是父类特别复杂,好比axios,那么会带来不少额外的开销。
咱们看一下中间对象有什么做用,实际上只起了一个隔离和原型重定向的做用。彻底能够用一个空对象实现这个功能
//实现中间对象 var fn = function() {} fn.prototype = Parent.prototype Children.prototype = new fn()
实际上,这个就是Oject.create()的实现
//Oject.create() Object.create = function(obj){ var fn = funcion(){} fn.prototype = obj reurturn new fn() }
如今既要继承实例属性,又要继承原型属性。
const Person = function (name) { this.name = name } Person.prototype.eat = function () { console.log('i am hungry,i want to eat!') } const Student = function (name) { Person.call(this,name) } Student.prototype = Object.create(Person.prototype) //注意这里!原型重定向的后遗症 const xm = new Student ('小明') xm.name //'小明' xm.eat() //i am hungry,i want to eat! console.log(xm) //Student {name: "小明"} // name: "小明" // __proto__: Person // __proto__: //这一层是个空对象,只有一个__proto__属性指向Person的原型 // eat: ƒ () // constructor: ƒ (name) // __proto__: Object
可是这里 xm.constructor.name // Person
这里由于原型重定向后没有重置construtor,xm自己没有construtor只能找咱们建立的空对象,空对象没有construtor因此继续往上找,找到了Person.prototype上的construtor为 Person
因此解决思路很简单,在改写原型链的时候重置一下constructor就好了
... var temObj = Object.create(Person.prototype) temObj.constructor = Student Student.prototype =temObj ... xm.constructor.name // Student ok搞定
既然new在“类”的建立里面必须使用,那么咱们就说一下new到底干了啥事情
1.建立一个对象o继承构造函数
2.让构造函数的this变为o,并执行构造函数,将返回值设置为k
3.若是k
//仿写new function new1(func) { var o = Object.create(func.prototype) var k = func.apply(o,arguments[1]) return typeof k === 'object'? k: o } const x = new1(Student,['张三']) x.name //'张三' x.eat //'i am hungry,i want to eat!'
咱们回过头再分析一下构造函数模式继承
const Person = function (name) { this.name = name } const Students = function (name) { Person.call(this,name) //this是student实例 } const xm = new Students('小明') //分析这里干了什么 console.log(xm) //Students {name: "小明"}
1.让空对象o继承Students(o能访问Students的原型)
2.student执行,执行Person的代码,this是o,而且传入name, o.name='小明'返回的k是undefined
3.返回o,也就是返回{name:'小明'}
class Person { } class Student extends person{ }
在babel es2015-loose模式下编译后的源码以下
"use strict"; function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } var Person = function Person() { }; var Student = /*#__PURE__*/ function (_person) { _inheritsLoose(Student, _person); function Student() { return _person.apply(this, arguments) || this; } return Student; }(person);
严格模式下,高级单例模式返回一个Student, 能够看到Person的实例属性用的Person的构造函数+apply继承的
原型属性用的_inheritsLoose这个方法继承的
_inheritsLoose方法貌似就是咱们以前说的原型属性继承的终极解决方案吧?
未完待续....