构造函数创建对象
function Person() {
}
var person = new Person();
person.name = 'Kevin';
console.log(person.name) // Kevin
prototype
每个函数都有一个 prototype 属性, 其实 函数的 prototype属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型。
每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个就是我们说的原型。每个对象都会从原型继承属性。
proto
每一个JavaScript对象(除了null)都具有的一个属性,叫做 **proto**这个属性会指向该对象的原型。
constructor
每个原型都有一个constructor属性指向关联的构造函数。
实例与原型
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查找不到,就出找原型的原型,一直找到最顶层为止。
原型的原型
原型对象就是通过 Ojbect构造函数生成的,结合之前所讲,实例的proto指向构造函数的prototype。
原型链
补充
constructor
function Person() {
}
var person = new Person();
console.log(person.constructor === Person); // true
当获取 person.constructor时,其实person中没有 constructor属性,当不能读取到constructor属性时,会从person的原型也就是 Person.prototype中读取,正好原型中有该属性。
proto
绝大部分浏览器都支持这个非标准的方法访问原型,然而它并存在于Person.prototype中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.proto 时,可以理解成返回了 Object.getPrototypeOf(obj)。
真的是继承吗?
继承意味着复制操作,然而JavaScript默认并不会复制对象的属性,相反,JavaScript只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。
继承实现:探究JS常见的6种继承方式
继承概念的探究
继承可以使得子类别具有父类的各种方法和属性, 也可以重写或者覆盖某些属性和方法,使其获得父类不同的方法。
JS实现继承的几种方式
第一种:原型链继承
每一个构造函数都有一个原型对象,原型对象又包含一个指向构造函数的指针,实例则包含一个原型对象的指针。
function Parent1() {
this.name = 'parent1';
this.play = [1, 2, 3]
}
function Child1() {
this.type = 'child2';
}
Child1.prototype = new Parent1();
console.log(new Child1());
第二种:构造函数继承(借助call)
function Parent1(){
this.name = 'parent1';
}
Parent1.prototype.getName = function () {
return this.name;
}
function Child1(){
Parent1.call(this);
this.type = 'child1'
}
let child = new Child1();
console.log(child); // 没问题
console.log(child.getName()); // 会报错
第三种:组合继承(原型与借助call)
function Parent3 () {
this.name = 'parent3';
this.play = [1, 2, 3];
}
Parent3.prototype.getName = function () {
return this.name;
}
function Child3() {
// 第二次调用 Parent3()
Parent3.call(this);
this.type = 'child3';
}
// 第一次调用 Parent3()
Child3.prototype = new Parent3();
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play); // 不互相影响
console.log(s3.getName()); // 正常输出'parent3'
console.log(s4.getName()); // 正常输出'parent3'
第四种:原型式继承
Object.create方法。参数一 作为新对象原型的对象, 为新对象定义额外属性的对象(可选)
多个实例的引用类型属性指向相同的内存,存在篡改的可能。
寄生式继承
let parent5 = {
name: "parent5",
friends: ["p1", "p2", "p3"],
getName: function() {
return this.name;
}
};
function clone(original) {
let clone = Object.create(original);
clone.getFriends = function() {
return this.friends;
};
return clone;
}
let person5 = clone(parent5);
console.log(person5.getName());
console.log(person5.getFriends());
寄生组合式继承
function clone (parent, child) {
// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
}
function Parent6() {
this.name = 'parent6';
this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () {
return this.name;
}
function Child6() {
Parent6.call(this);
this.friends = 'child5';
}
clone(Parent6, Child6);
Child6.prototype.getFriends = function () {
return this.friends;
}
let person6 = new Child6();
console.log(person6);
console.log(person6.getName());
console.log(person6.getFriends());