在掘金第一篇文章,都是自己学习总结,也是给公司组员培训分享的干货,有兴趣的点个好,有问题的劳烦指正。 鼓励,js栗子代码拷贝到谷歌浏览器console下调试尝试。
原型模式
可以通过原型模式来介绍js是怎么样的一个语言
概述:原型模式是用于创建对象的一种模式,要符合四个原则。
一般,如果我们想要创建一个对象, 一种方法是先指定它的类型,然后通过类来创建这个对象,例如java,如下
// java 创建对象
// 先指定它的类型
public class Duck { // 鸭子类
public void makeSound(){
System.out.println( "嘎嘎嘎" );
}
}
public class Test {
public static void main( String args[] ){
// 然后通过类来创建这个对象
Duck duck = new Duck();
}
}
// js创建对象
let a = {};
let b = new Object();
原型模式选择了另外一种方式,我们不再关心对象的具体类型,而是找到一个对象,然后通过克隆来创建一个一模一样的对象。
So,在 JavaScript 语言中不存在 类 的概念,对象不是从类中创建出来,所有的js对象都是从Object对象上克隆而来的。
理解时候,可以把原型替换成模板二字来理解
四个原则
原则一
所有的非基本数据类型都是对象(即排除数字,布尔,字符串,null,undefined)
JavaScript 中的根对象是 Object.prototype 对象。Object.prototype 对象是一个空的对象。我们使用的每个对象,都是从 Object.prototype 对象克隆而来的, Object.prototype 对象被叫做这些对象的原型。
// 创建空对象
var obj1 = new Object();
var obj2 = {};
// 比较对象
console.log( Object.getPrototypeOf( obj1 ) === Object.prototype ); // 输出:true
console.log( Object.getPrototypeOf( obj2 ) === Object.prototype ); // 输出:true
// 顶层是null
Object.prototype.__proto__
原则二
要得到一个新对象,就是找到一个对象,把它作为原型,然后克隆它
所以,我们只要显式地调用 let obj1 = new Object() 或 var obj2 = {}。
此时,js内部会从 Object.prototype 上面克隆一个对象出来,我们最终得到的就是这个clone的对象。
js函数可以作为普通函数,也可以作为构造器。当使用 new 运算符来调用函数时,函数就是一个构造器。
通过下面代码,可以模拟new操作符的运算过程:
function Person( name ){
this.name = name;
};
Person.prototype.getName = function(){
return this.name;
};
var objectFactory = function(){
var obj = new Object();// 从 Object.prototype 上克隆一个空的对象
// shift 弹出类数组的第一个值
Constructor = [].shift.call( arguments ); // 取得外部传入的构造器,此例是 Person。
// 这就是为什么new对象得到的变量,和对象.prototype 进行 === 比较地 true 的原因
obj.__proto__ = Constructor.prototype; // 指向正确的原型。遵守:对象会记住它的原型 这个原则
console.log('obj1', obj);
Constructor.apply( obj, arguments ); // 借用外部传入的构造器给 obj 设置属性
console.log('obj2', obj);
return obj; // 确保构造器总是会返回一个对象
};
var a = objectFactory( Person, 'sven' );
console.log( a.name ); // 输出:sven
console.log( a.getName() ); // 输出:sven
console.log( Object.getPrototypeOf( a ) === Person.prototype );// 输出:true
// 分别调用下面两句代码产生了一样的结果:
// var a = objectFactory( A, 'sven' );
// var a = new A( 'sven' );
原则三
对象会记住它的原型
到此为止,js不具备java一样的继承能力,因为新的对象只是clone而来,并没有父类的概念,而继承是面向对象语言不可缺少的基本能力。为了使js也具有继承的能力,js语言设计者实现了这第三个原则。
JavaScript 通过给每个对象提供了一个名为__proto__的隐藏属性,将__proto__属性指向它的构造器的原型对象。
到这里,clone得到的新对象就知道了自己的原型(模板)对象是哪个。就可以实现继承等特性。
__proto__就是对象自己跟它clone的模板对象(原型)联系起来的纽带
目前我们一直在讨论“对象的原型”,就 JavaScript 的真正实现来说,其实并不能说对象有原型,而只能说对象的构造器有原型。对于“对象把请求委托给它自己的原型”这句话,更好的说法是对象把请求委托给它的构造器的原型
怎么理解这句话呢,见下方示例:
function Person( name ){
this.name = name;
};
let a = new Person('gaocc');
console.log('a:',a.name); // 打印 a: gaocc
// 对象a的构造器是Person,a请求委托寻找name是通过Person的原型来追溯找到的。
// 即:对象把请求委托给它的构造器的原型
这时候,再来看原则二的示例,obj.__proto__ = Constructor.prototype;这一行就是把构造器的原型赋值给对象的__proto__,实现对象记住自己原型的目的。
原则四
如果对象无法响应某个请求,它会把这个请求委托给它的构造器的原型
这条规则是原型模式的继承精髓。
JavaScript 的对象最初都是由 Object.prototype 对象克隆而来的,但构造器的原型并不仅限于 Object.prototype 上,而是可以动态指向其他对象。
观察下面原型继承的示例:
var obj = { name: 'sven' };
var A = function(){};
A.prototype = obj; // 对象挂载到构造器的原型上
var a = new A();
console.log(a.name) // 输出:sven
JS做的事情如下:
1、首先,尝试遍历对象 a 中的所有属性,但没有找到 name 这个属性
2、查找 name 属性的这个请求被委托给对象 a 的构造器的原型,它被 a.__proto__ 记录着并且指向 A.prototype,而 A.prototype 被设置为对象 obj
3、在对象 obj 中找到了 name 属性,并返回它的值
观察下面一个“类”继承自另外一个“类”的效果示例:
var A = function(){};
A.prototype = { name: 'sven' };
var B = function(){};
B.prototype = new A(); // B继承A //
var b = new B();
console.log( b.name ); // 输出:sven
1、首先,尝试遍历对象 b 中的所有属性,但没有找到 name 这个属性
2、查找 name 属性的请求被委托给对象 b 的构造器的原型,它被 b.__proto__ 记录着并且指向 B.prototype,而 B.prototype 被设置为一个通过 new A()创建出来的对象
3、在该对象中依然没有找到 name 属性,于是请求被继续委托给这个对象构造器的原型 A.prototype
4、在 A.prototype 中找到了 name 属性,并返回它的值
继承总是发生在对象和对象之间而上述操作,和把 B.prototype 直接指向一个字面量对象相比,通过 B.prototype = new A()形成的原型链比之前多了一层。但二者之间没有本质上的区别,都是将对象构造器的原型指向另外一个对象
总结
1、所有的非基本数据类型都是对象
2、要得到一个新对象,就是找到一个对象,把它作为原型,然后克隆它
3、对象会记住它自己的原型
4、如果对象无法响应某个请求,它会把这个请求委托给它的构造器的原型
原型模式是一种设计模式,也是一种编程泛型,它构成了 JavaScript 这门语言的根本
总结:原型模式是一种设计模式,也是一种编程泛型,它构成了 JavaScript 这门语言的根本
tip:后续有时间会断断续续分享自己的知识总结,大部分是给组员培训的干货,有兴趣的点个关注