- 创建对象的方法:①字面量 ②通过构造函数 ③Object.create
- 原型、构造函数、实例、原型链
- instanceof的原理
- new运算符
- 继承的几种方式缺点
prototype属性:原型对象,默认对应这一个空对象,因此每一个prototype都是不相等的
显式原型和隐式原型
- 每一个函数都有一个prototype属性,就是显式原型属性
- 每一个实例对象都有一个
__proto__
,就是隐式原型 - 实例对象的隐式原型等于对应的构造函数的显示原型的值
属性设置和屏蔽
var anotherObject = {
a:2
}
var myObject = Object.create( anotherObject );//创建一个新对象myObject,并把这个新对象的Prototype属性关联到指定的anotherObject对象上
console.log(anotherObject.a);//2
console.log(myObject.a);//2,现在这个a存在myObject的原型链上
anotherObject.hasOwnProperty('a');//true
myObject.hasOwnProperty('a');//false
anotherObject.a++
console.log(myObject.a);//3
myObject.a++;//隐式屏蔽,先通过Prototype属性查找并获取属性a的值3,然后给这个值加1,用put方法将值4赋给myObject中新建的屏蔽属性a
console.log(anotherObject.a);//3
console.log(myObject.a);//4
总结:
- 函数的prototype属性,在定义函数是自动添加的,默认值是一个空对象
- 对象的__proto__属性,创建对象的时是自动添加的,默认值是构造函数的prototype属性:
pensor.__proto__ === Person.prototype
- 原型对象本身是一个普通对象,而普通对象的构造函数都是
Object
:Person.prototype.__proto__=Object.prototype
- 所有的构造器都是函数对象,函数对象都是Function构造产生的:
Object.__proto__===Function.prototype
- Object的原型对象也有__proto__属性指向null,null是原型链的顶端:
Object.prototype.__proto__==null
总结: - 一切对象都是继承自Object对象,Object 对象直接继承根源对象null
- 一切的函数对象(包括 Object 对象),都是继承自 Function 对象
- Object 对象直接继承自 Function 对象
- Function对象的__proto__会指向自己的原型对象,最终还是继承自Object对象
js中对原型的修改和重写
function Person(name) {
this.name = name;
this.age = 18;
this.sayName = function() {
console.log(this.name);
}
}
// 第二步 创建实例
var person = new Person('person')
原型链
任何一个实例,通过原型链都可以找到它上面的原型,该原型对象中的方法和属性,可以被所有的原型实例共享
instanceof:用于判断实例属于哪个构造函数(原理是判断实例对象的__proto__
属性和构造函数的prototype
属性是否为同一个引用)
foo instance of Foo的结果为true,因为foo.__proto__===Foo.prototype是true
foo instance of Object的结果为true,因为Foo.prototype.__proto__===Object.prototype为true
用constructor
属性判断继承关系更为准确
例如:A继承了B,B继承了C,怎么判断a是由A直接生成的实例
答:因为a.__proto__.constructor===A
为true,但是a.__proto__.constructor===Object
的结果为false
参考文献:
类的定义、实例化
类的定义
- 用构造函数模拟类(传统写法)
function Animal1(){
this.name = 'lys';
}
- 用class声明(ES6声明)
class Animal2{
constructor(name){
this.name = name
}
}
实例化:不管用哪种方式定义,实例化都用new操作
继承
继承的本质是原型链。常见问题有继承的方式有几种,每种形式的优缺点是什么?
- 方式1 借助构造函数:在子类构造函数中利用
call
、apply
和bind
修改this执行,将父类中的this指向子类,将父类实例属性挂载到子类实例上,实现继承
function Parent1() {
this.name = 'parent1 的属性';
}
function Child1() {
Parent1.call(this); //【重要】此处用 call 或 apply 都行:改变 this 的指向
this.type = 'child1 的属性';
}
console.log(new Child1);
缺点:子类无法继承父类的原型
- 方式2 通过原型链实现继承:将子类的原型(prototype)指向父类的实例对象
/*
通过原型链实现继承
*/
function Parent() {
this.name = 'Parent 的属性';
}
function Child() {
this.type = 'Child 的属性';
}
Child.prototype = new Parent(); //【重要】
缺点:如果修改child1实例的对象属性,child2实例中的对象属性也会跟着改变,基本数据类型不会发生改变
- 方式3 构造函数+原型链组合方式: 即可以继承父类原型的内容,也不会造成原型里属性的修改。缺点是让父亲的构造方法执行两次
/*
组合方式实现继承:构造函数、原型链
*/
function Parent3() {
this.name = 'Parent 的属性';
this.arr = [1, 2, 3];
}
function Child3() {
Parent3.call(this); //【重要1】执行 parent方法
this.type = 'Child 的属性';
}
Child3.prototype = new Parent3(); //【重要2】第二次执行parent方法
var child = new Child3();
参看链接:
面向对象