一:在javascript中,对象之间永远不会完全相等,除非他们是同一个对象,因此即使创建一个具有完全相同成员的同类对象,它也不会与第一个对象完全相同。
var o1 = {
name : "Nick"
};
var o2 = {
name : "Nick"
};
console.log(o1 === o2); // false
只有 var q1 = {}; var q2 = q1; q2 === q1; // true
二:构造函数(在构造函数的静态属性中缓存实例,这是一种很好的实现方法,唯一的缺点在于instance属性是公开可访问的属性,在外部可能修改)
function Pet(){
this.legs = 4;
}
var dog = new Pet();
var cat = new Pet();
console.log(dog === cat); // false
实现方法:
// 在构造函数的静态属性中缓存该实例。Jvascript中的函数也是对象,因此他们也可以有属性。
// 可以使用类似Universe.instance的属性并将实例缓存在该属性中。
//
function Universe(){
// 我们有一个现有的实例嘛?
if(typeof Universe.instance === "object"){
return Universe.instance;
}
// 正常进行
this.start_time = 0;
this.bang = "Big";
// 缓存
Universe.instance = this;
// 隐式返回
//return this;
}
var uni = new Universe();
var uni2 = new Universe();
console.log(uni === uni2); // true
说明: 构造函数会隐式返回this,在构造函数中this指向新创建的实例对象,Universe.instance = this,也就是隐式返回Universe.instance。
每次执行之前都会检测Universe.instance是否存在,存在了就返回的同一个东西——Universe.instance
但是有个缺点是:Universe.instance是暴露的,他是Univer 的一个公开属性
在控制台输入Universe.instance;// Universe{start_time: 0, bang: "Big"}
Universe.instance.start_time = 1;
uni1.start_time; // 1 (uni1.start_time很轻易的就呗修改了,同样改变uni1.start_time Universe.instance也改变了)
三: 闭包中的实例
// 闭包的实例
function Universe(){
// 缓存实例
var instance = this;
// 正常进行
this.start_time = 0;
this.bang = "Big";
// 重写该构造函数
Universe = function(){
return instance;
};
}
var uni = new Universe();
var uni2 = new Universe();
console.log(uni === uni2);// true
说明:
为了改进上面的缺陷,这里将instance作为Universe的一个私有静态成员,外面不能再访问和修改了。
uni === uni2之所以完全相等,因为 uni 是隐式返回 instance, 而 uni2 是重写 Universe 函数之后返回 instance
PS :
今天又有人在讨论:addEvent的那个典型的优化,函数类似下面这段代码
ex1:
function add(a){
console.log("begin!!");
if(true){
console.log("是否再执行 if 条件检查???");
add = function( a ){
console.log(a);
};
add(a); // 非常重要的一个步骤!!!!
}
下面我们执行函数 add(1);// "begin!!" "是否再执行 if 条件检查???" 1
继续:add(1);// 1
继续:add(1);// 1
可以看到除了第一次,后面不管执行多少遍,都不需要在检查是否满足条件了。因为重写了那个函数,这提高了函数性能
这两者有着异曲同工之妙,都是重写了函数。
question1:但是,目前还是存在问题,如下测试:
// 向原型添加属性 Universe.prototype.nothing = true; // A var uni1 = new Universe(); Universe.prototype.everything = true;// B var uni2 = new Universe();
按照正常的道理:
function Person(){
this.name = "nick";
}
Person.prototype.huhu = "huhu";
var p = new Person();
p; // {name:"nick", huhu : "huhu"}
Person.prototype..like = "like";
p;// {name: "nick", huhu: "huhu", like : "like"}
先创建一个实例对象,后面添加的 prototype 都能够实时的反应到所有的实例对象中。
这里第一次new之后,Universe已经改变了(A 跟 B 不是同一个 Universe ->> 导致uni1没有everything属性),在那之后添加的原型属性都不能被真的添加,因为重写的Universe直接return instance 私有变量(导致uni2 也没有everything属性)
四:
function Universe(){ // 缓存实例 var instance; // 重写构造函数 Universe = function(){ return instance; }; // 保留原型属性 Universe.prototype = this; console.log(Universe.prototype.nothing) // true // 实例 instance = new Universe(); // 新的Universe console.log(instance.constructor === Universe) // false // Universe.prototype = this , 而 this 指向实例对象, uni1.constructor是 外层Universe ,这里的Universe是新的 所以不相等 // 重置构造函数指针 instance.constructor = Universe; // 指向新Universe // 所有功能 instance.start_time = 0; instance.bang = "Big"; return instance; } Universe.prototype.nothing = true; // uni1.nothing = true; var uni1 = new Universe(); Universe.prototype.everything = true;// this.everything = true; var uni2 = new Universe(); console.log("is equal:?" , uni1 === uni2)// true
说明:
1. uni1 是新的 Universe 构造的: instance = new Universe(); 返回insatnce
2. uni2 也是function Universe(){ return instance;} 只不过此时的构造函数并没有创建对象,而只是通过闭包的形式返回了之前uni创建的instance,所以这2个instance是一样的
3. 一开始设置Universe.prototype.nothing = true; // this.nothing = true; --> Universe.prototype.nothing = true;--> instance.nothing = true;
4. new后面 设置Universe.prototype.everything = true;// instance.everything = true; 在未定义everything属性之前访问不到everything,但是一旦change了instance 所有的都跟着变,因为他们指向同一个instance.
这里的返回值都是 instance, 他作为了新Universe的一个实例对象,所有的对象都是instance这一个其实。原来不能原型共享是因为Instance只是一个静态的,这里instance= new Universe(); 可以通过Universe.prototype.xxxx 来同步改变instance.
五:
var Universe; (function(){ var instance; Universe = function Universe(){ if(instance){ return instance; } instance = this; // 所有功能 this.start_time = 0; this.bang = "Big"; }; })(); var uni1 = new Universe(); var uni2 = new Universe();
说明:在第一次调用构造函数时,它会创建一个对象,并且使得私有instance指向该对象。从第二次调用之后,该构造函数仅返回该私有变量
知识点补充:
一个构造函数不是 return this; 的话,返回的就不是这个构造函数的实例。
ex1:
function Parent(){
return [];
}
var pp = new Parent();
pp; // []
pp.constructor; // function Array() { [native code] }
实例的constructor指向原型,而不是构造函数。
ex2:
function Person(){}
var pp1 = new Person();
pp1.constructor === Person.prototype.constructor;
Person.prototype = [];
pp1.constructor === Person.prototype.constructor;// false
那么,怎么检查一个实例的属性是继承的构造函数的函数还是构造函数的原型呢?
ex3:
function PP(){}
PP.prototype.name = "lk";
var pp = new PP();
"name" in pp && !pp.hasOwnProperty("name"); //返回值为true既是继承的原型的(hasOwnproperty()不会检查原型链!!并且只会针对实例检查)