原型链(Prototype chain)的定义:每个对象拥有一个原型对象,对象以其原型为模板,从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层以此类推,这种关系被称为原型链。
在js中,函数可以有属性,每个函数都有一个特殊的属性叫做原型(Prototype)。
prototype属性:继承成员被定义的地方(继承的属性和方法是定义在prototype属性之上的。)
- prototype属性的值是一个对象,我们希望被原型链下游的对象继承的属性和方法,都存在这里。
- Object.is()、Object,keys()以及其他不在prototype对象内的成员,不会被对象实例或继承自Object的对象类型所继承,这些方法仅能被Object构造器自身使用。
先看一个简单的例子
从上面的代码中我们可以看出,方法show并不是对象obj的直接属性,但也可以被调用。从表面上来看,它是从另一个对象(MyClass.propotype)的属性继承而来的。
对原型继承的形式上的理解:
类名.propotype.方法名 = function(parameter) { ... }
注:当继承的函数被调用时,this指向的是当前继承的对象,而不是继承的函数所在的原型对象。
ES5继承
在ES5中的继承,上一篇博客里已经提到了,我们再来写一个ES5继承的例子。
构造函数生成实例的执行过程:
- 当使用了构造函数,并且new 构造函数(), 后台会隐式执行new Object()创建对象。
- 将构造函数的作用域给新对象,而函数体内的this就指向新建的对象。
- 执行构造函数的代码。
- 返回新对象。
ES6继承
在ES6中引入了class类的概念,通过class关键字可以定义类,该关键字的出现使得其在对象写法上更加清晰,更像是一种面向对象的语言。class类就是ES5构造函数的语法糖。
- 在类中声明方法的时候,不要加function关键字
- 方法之间不要用逗号分隔
constructor方法是类的构造函数的默认方法,它往往负责做一些初始化的工作,通过new命令生成对象实例时,自动调用该方法。如果该方法没有显示定义,那么会隐式生成一个constructor方法,所以即使你没有添加构造函数,构造函数也是存在的。constructor方法默认返回实例对象this,但是也可以指定constructor方法返回一个全新的对象,让返回的实例对象不是该类的实例。
注:class类内部默认开启严格模式,要注意this丢失的问题,发生此类问题使可以考虑使用bind()解决。
在上面的例子中,say方法虽然是卸载class内部的,但是它实际上还是定义在原型链上的,下面我们来实验一下。
源代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function () {
return "My name is " + this.name + ", I'm " + this.age + " years old.";
}
var obj = new Person("Tom", 9)
console.log(obj.say())
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
say() {
return "My name is " + this.name + " I'm " + this.age + " years old."
}
}
var obj = new Person("Tom", 9)
console.log(obj.say());
</script>
</body>
</html>