这篇总结,主要是看了一名【合格】前端工程师的自检清单这篇文章,照着单子来整理总结下,这篇介绍的是「JavaScript中的原型和原型链」这个小结,是按着其中的问题进行归档整理的,其中参考了很多前辈总结的知识,在这里表示感谢!
- JavaScript基础自检之原型和原型链
- JavaScript自检之变量和类型
- JavaScript基础自检只作用域闭包
理解原型设计模式以及JavaScript中的原型规则
-
原型
[[Prototype]]是对象中的一个内置属性,表示对对象的引用;除了顶级Object的[[prototype]],其他的[[Prototype]]均不为null。
-
原型链
内置属性[[Prototype]],表示对对象的引用,而其引用的对象也有[[Prototype]],以此类推,直到顶级对象Object的[[prototype]],而这些[[Prototype]]的构成的集合,就被称为原型链。
-
原型规则
-
所有引用类型(数组、对象以及函数),都具有对象的特征,可自由扩展属性;
-
所有的引用类型,都具有一个「proto」,属性值是一个普通对象;
-
所有函数,都有一个prototype属性,属性值是一个指向原型对象的指针;
-
所有引用类型,其隐式原型指向其构造函数的显示原型;
-
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__中去找;
-
-
原型对象
函数的prototype属性指向的对象,即被成为原型对象。该对象包含了通过构造函数调用创建对象的所有共享的方法和属性。
-
创建对象的方式
-
工厂模式
在函数内创建一个对象,给对象赋予属性及方法再将对象返回
function Person() { var person = new Object(); return person; } var n = Person(); -
构造函数模式
无需在函数内部创建新对象,使用this
function Person(name) { this.name = name // .... }var p = new Person(); -
原型模式
函数中不对属性进行定义,利用prototype属性进行定义。
function Person(name) { Person.prototype.name = name; } var p = new Person(); -
混合模式
原型模式 + 构造函数模式
function Person(name) { // 定义实例属性 this.name = name; } Person.prototype.sayname = function() { // 定义公共属性和方法 return this.name; } var n = new Person(); -
动态原型模式
将所有信息封装在构造函数中,而通过构造函数初始化属性,动态掌握方法是否需要关联到原型中
function Person(name) { this.name = name; if(typeof Person._ddd === 'undefined') { Person.prototype.sayname = function() { return this.name; } Person._ddd = true; } } -
寄生构造函数模式
封装创建对象的代码,然后返回新的对象
function Person(name) { var n = new Object(); n.name = name; /// ... return n; } var n = new Person(); -
稳妥构造函数
道格拉斯.克罗克福德发明了JavaScript中稳妥对象(durable objects)这一概念。所谓稳妥是指没有公共属性,而且其方法也不引用this的对象。
稳妥构造函数遵循与寄生构造函数类似的模式,但有2点不同:
1、新创建对象的实例方法不引用this
2、不使用new操作符调用构造函数
实现:
function Person(name) { var o = new Object(); // TODO 定义私有变量和方法 // 添加方法 o.sayName = function() { alert(name); } return o; }
-
实现一个方法:能简单模拟instanceof的功能
-
instanceof的polyfill版本
function instanceofPolyfill(instance, obj) { //instance->实例;obj->func let type = typeof instance; if(type == 'null' || type == 'undefined') { throw new Error('instance must be instance Object'); } if(type == 'string' || type == 'number' || type == 'boolean') { return false; } if(typeof obj != 'function') { throw new Error('obj must be Function'); } console.log('obj是', obj.constructor.prototype); var _prototype = obj.constructor.prototype; if(obj.prototype.isPrototypeOf(instance)) { // obj出现在instance的原型链上 return true; } return false; }
实现继承的几种方式以及他们的优缺点
-
类式继承
function Animal() { this.name = 'animal'; } function Dog() { this.name = 'dog' } var _dog = new Animal(); // 类式继承有个不足:对于引用类型的数据,一个实例修改值,则所有实例对应的该值都会变。 -
构造函数继承
function Animal() { this.name = 'erer'; } function Dog() { Animal(this, arguments); // 注:arguments为函数中的参数对象 } var _dog = new Dog(); // 构造函数继承,获取不到父类的公共方法(非) -
组合继承
function Animal() { this.name = '12312'; } function Dog() { Animal(this, arguments); } var _dog = new Animal(); // 实例化 子类 时,会调用两次父类的构造函数 -
寄生组合式继承
function Animal() { this.name = '12312' } function Dog() { Animal(this, arguments); } Dog.prototype = Object.create(Animal); Dog.prototyp.constructor = Dog; -
extends继承
ES6中新引入的继承方式。
属性设置和屏蔽
对一个对象设置属性,不仅仅是简单的添加一个新属性或者修改现有的属性,下面以
myobject.foo = '123';
为例,分情况讲解下这个过程:
-
myobject中包括foo这个普通的属性,那么就会直接修改现有的属性;
-
foo不存在于myobject,并不存在其[[Prototype]]中,那么就会直接添加为myobject;
-
foo存在于myobject的原型链行,那么:
-
如果为普通的属性,并且是可写,那么就会添加到myobject,并且foo为屏蔽属性
-
如果为普通的属性,并且是不可写,在严格模式下,会报错;在非严格模式下,不会进行任何操作:不会覆盖属性,不会添加到myobject中。
-
如果是一个setter,那就一定会调用这个setter。foo不会添加到myobject,也不会重新定义foo这个setter。
-
new 运算符调用,会发生什么?写一个函数。
function newPolyfill(obj) {
// obj为要new的构造函数
// 处理参数
var args = [].slice.call(arguments);
args.shift();
var _obj = Object.create({});
_obj.__proto__ = obj.prototype;
var res = obj.apply(_obj, args);
if((res != null && typeof res === 'object') || typeof res === 'function'){
return res;
}
return _obj;
}
理解es6 class构造以及继承的底层实现原理
根据MDN文档结合我自己的理解,es class是现有JavaScript原型继承的扩展,原理还是原型扩展。可以参考实现继承的几种方式以及他们的优缺点