理解原型
js分为函数对和普通对象,每个对象都有__proto__属性,但是只有函数对象(非箭头函数)才有prototype属性。
创建一个函数(非箭头函数),就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构造函数。
XXX.prototype 存储了 XXX 对象的共同属性,这样做的好处是:无需重复声明共有属性
function Dog(name) {
this.name = name
}
Dog.prototype.wangwang = function(){ console.log('汪汪') }
Dog.prototype.run = function(){ console.log('狗在跑') }
let dog = new Dog('小白')
dog.run() // 狗在跑
dog.wangwang() // 汪汪
console.log(Dog.prototype.constructor === Dog) // true
__proto__ 和 prototype
原型与共有属性的关系:x.prototype:... 原型的地址
X.__proto__ === 其构造函数的.prototype
javascript 的继承
一个对象系统的继承特性有三种实现方案,包括基于类、基于原型和基于元类。其中,JS使用了原型继承来实现对象系统。
早期的Javascript中并没有类,只是用 constructor 来实现类的某些功能。知道ES6才开始正式支持类,在语法上,类Class是一段静态描述,它必须先被确定好,然后由引擎去装载和装配(而在实现时,Javascript的类继承系统仍然是基于原型的————这是一种罕见的实现模型)。
不用 class 实现继承
function Animal(color){
this.color = color
}
Animal.prototype.move = function(){} // 动物可以动
function Dog(color, name){
Animal.call(this, color) // 或者 Animal.apply(this, arguments)
this.name = name
}
// 下面三行实现 Dog.prototype.__proto__ = Animal.prototype
function temp(){}
temp.prototype = Animal.prototype
Dog.prototype = new temp()
Dog.prototype.constuctor = Dog
Dog.prototype.say = function(){ console.log('汪')}
使用类继承体系
ES6中,class可以使用继承
super的出现是为了填补原型继承的一项不足:无法有效调用父类方法。
class Book {
constructor(title, pages, isbn) {
this.title = title;
this.pages = pages;
this.isbn = isbn;
}
printIsbn() {
console.log(this.isbn);
}
}
class ITBook extends Book {
constructor (title, pages, isbn, technology) {
// 通过 super 关键字引用父类的构造函数
super(title, pages, isbn);
this.technology = technology;
}
printTechnology() {
console.log(this.technology);
}
}
let jsBook = new ITBook('学习 JS 算法', '200', '1234567890', 'JavaScript');
console.log(jsBook.title);
console.log(jsBook.printTechnology());
当父类(super)是内置的Object()时,extends Object可以省略 当不需要指定构造过程时,construct(){...}声明也可以省略
对于使用class关键字的类声明过程来说,最明显的收益就是可以使用extends关键字来声明父类
class MyObjectEx extends MyObject {
...
}
// 这基本上替代了构造函数声明风格的如下代码
MyObjectEx.prototype = new MyObject()
MyObjectEx.prototype.constructor = MyObjectEx
类是静态的声明
类是静态的声明,意味着类继承关系的构建过程也是静态的,是在语法分析期就决定了的。与此相关的,这也意味着类声明语法中的方法或属性存储器只是(对象方法的)声明,而不是函数,因此也就不能在声明内直接引用它们的名字。
//使用构造器声明
function MyObject() {
//作为构造器,这里可以直接引用 MyObject 这个函数名
console.log(typeof MyObject)
}
//使用类声明语法
class MyObjectEx {
constructor() {
console.log(typeof constructor) //undefined
}
foo() {
console.log(typeof foo) //undefined
}
}
混淆知识点
- window是谁构造的?
Window,可以通过constructor属性看出构造者(window.constructor)
window.__proto__=== Window.Prototype
- window.Object 是谁构造的?
window.Function
因为所有的函数都是window.Function构造的 window.Object.constructor === window.Function
- window.Function 是谁构造的?
window.Function,浏览器构造了Function,然后指定它的构造者是自己
window.Function.constructor === window.Function
拨乱反正
一、xxx的原型
- Object的原型是Object.
__proto__对 - Object的原型是Object.prototype 错
二、Object.prototype 是根对象
总结
- 对象.
__proto__=== 其构造函数.prototype - Object.prototype 是所有对象的(直接或间接)原型
- 所有函数都是由 Function 构造的 任意函数.
__proto__= Function.prototype