文章目录
实例、原型、构造函数的关系:
function Parent () {
this.name = 'name'
}
var person = new Parent()
1.概念:
构造函数: js中所有的函数都可以作为构造函数,前提是被new操作符操作。通常函数名首字母大写
实例: 接收了被new实例化后新建的对象
原型:构造函数都有一个prototype属性,这个属性指向原型对象。
在上述代码中 构造函数是Person,实例是person 。
2.关系:
new操作符作用于构造函数,得到实例。实例对象有一个_proto_(每个对象都有_proto_)属性指向该实例的构造函数对应的原型对象。
构造函数的prototype(所有函数都有的属性)指向原型对象。原型对象例也有一个constructor属性,指向构造函数。
函数的原型对象组成:
function fn(){}
console.log(fn.prototype) //输出如下图
3.确定原型和实例的关系:
1.instanceof : 只要实例和原型链中出现过的构造函数都会返回true
person instanceof Person // true
2.isPrototype():只要是原型链中出现过的原型,都可以说是该原型链所派生的原型实例,会返回true。
Person.prototype.isPrototype(person)
补充:检查对象自己定义的属性,需使用所有对象从 Object.prototype 继承的 hasOwnProperty 方法,它不会遍历原型链的方法. 用法p.hasOwnProperty(‘name’) // 返回布尔值
补充:js 写new操作符的工作原理:
var newObj = function(func){
var o= {} // 1.新建一个对象
o.prototype = func.prototype //指定原型
var k =func.call(o); // 执行构造函数,this指向该实例
if(typeof k === 'object'){ //构造函数返回了对象,最后返回该对象
return k;
}else{
return o;
}
}
var parent1 = newObj(Parent)
4.原型链
当我们从一个对象里找某个属性,如果在当前对象没找到,那么会从通过_proto_属性一直往上找,如果找到了Object对象上还没找到的话,则这个属性才是真的不存在,否则只要找到了,这个属性都是存在的。像这样让原型对象等于另一个类型的实例,通过_proto_将对象和原型联系起来组成的链条关系叫做原型链。
原型链关系图(图源网络)
继承:
1.构造函数实现继承
核心代码
function P(){}
function C(){P.call(this)}
优点:子类构建实例可以向父类传参
缺点: 子类实例的方法每次需要单独创建
function Parent(){
this.name='zhangsan'
}
function Child(){
Parent.call(this)
}
Parent.prototype.age = 29
var c1 = new Child()
console.log(c1.name) //zhangsan
console.log(c1.age) // undefined
缺点:实例对象c1访问不到父级原型上的(age)属性
2.原型链实现继承:
function P(){}
function C(){}
C.prototype =new P()
核心: 将父类实例作为子类实例的原型
优点:能继承到原型上的所有属性
缺点: 即原型链继承引用类型属性时,在任意实例对象上改变该属性,所有实例对象属性都会变化。(父子类构建实例不能向父类传参
function Parent(){
this.name = 'parent'
this.play = [1,2,3]
}
function Child(){
this.type = 'child';
}
Child.prototype = new Parent(); //把父类实例赋值给子类原型
var child1 = new Child();
console.log(child1.play) // [1,2,3]
child1.play[0]=0 // 改变实例对象child1里的数据,child2对应也会变。
var child2 = new Child()
console.log(child2.play) //[0,2,3]
3.组合继承简化
核心:结合原型链和构造继承,兼具了二者优点
缺点:调用了两次父类构造函数,会造成性能上的浪费
function Parent(){}
function Child(){
Parent.call(this) // 第二次调用
}
Child.prototype = new Parent() // 第一次调用Parent
4.使用Object.create实现继承
function Parent(){
this.name = 'parent1'
this.play = [1,2,3]
}
function Child(){
Parent.call(this);
this.type = 'parent2';
}
//var obj={}
//obj.prototype=Parent.prototype
//Child.prototype = obj 这三句话等价于接下来的一句
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor=Child
console.log(Child.prototype.constructor)
console.log(Parent.prototype.constructor) 输出如下
注:Object.Create() 是es5引入的方法,可以调用该方法来创建一个新对象, 使用现有的对象来提供新创建的对象的__proto__。即新对象的原型就是()传入的第一个参数.
语法:Object.create(proto, {propertiesObject})
proto: 新创建对象的原型对象。
propertiesObject: 可选,添加新创建对象的自定义属性
var b = Object.create(a);
//原型链 b ---> a ---> Object.prototype ---> null
console.log(b.属性); // (继承而来)
5.ES6 的Class Extends实现继承 (待补充)
而ES6先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
super关键字可以调用父类中的函数。
class A {}
class B extends A {
constructor() {
super();
}
}
// 定义父级
class Polygon {
constructor(height, width) {
this.name = 'Polygon';
this.height = height;
this.width = width;
}
sayName() {
console.log('Hi, I am a ', this.name + '.');
}
}
class Square extends Polygon {
constructor(length) {
super(length, length);
this.name = 'Square';
}
get area() {
return this.height * this.width;
}
set area(value) {
this.area = value;
}
}
let s = new Square(5);
s.sayName();
console.log('The area of this square is ' + s.area);