深入学习jquery源码之创建科学、复用率高的对象

时间:2023-02-23 22:00:10


常规创建对象的方式

通过{},[] 来定义数组和对象

1.{ } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或是函数。

2.[ ]中括号,表示一个数组,也可以理解为一个数组对象。

3.{ } 和[ ] 一起使用,我们前面说到,{ } 是一个对象,[ ] 是一个数组,我们可以组成一个对象数组

调用起来对象的属性用.(点)叠加,数组用 [下标] 来访问。

通过”字面量“的方式来创建对象

var person = {
name: "dongjc",
age: 32,
Introduce: function () { alert("My name is " + this.name + ".I'm " + this.age); }
};
person.Introduce();
person.worker = 'coding'; //丰富成员信息

调用方式

var ren ={};
ren.name="张三";
ren.sex="男";
ren.eat=function () {
alert("吃饭");
}
alert(ren.name);
alert(ren["name"]);

当我们创建一个对象 ren,会在栈内存中保存一个地址,栈为长度不可变的地址。

而栈中的地址就对应堆中的存储地址。堆中的存储地址,只要实例化会在堆中开辟一块空间,地址就是栈的地址,内容就是实例化对象里面的内容,如name,sex,eat。可以通过地址引用,访问里面的属性和方法。

当我们再实例化一个对象,又会保存另一个地址及开辟一块空间。

代码段,共同的属性或方法放在代码段中,不在堆中。只执行一次,节省内存空间。

 

通过object方式创建对象,动态添加属性和方法

var p=new Object(); 
p.name="Jack"; // 动态的添加属性
p.func=speak; // 动态的添加方法
alert(p.name);
p.func("Hello,Hello,大家好!");

delete p.name;
alert(p.name);
delete p.func;
p.func("Hello,Hello,大家好!");

p.name=undefined;
p.func=undefined;
alert(p.name);
p.func("Hello,Hello,大家好!");

 

通过Function函数来创建对象

小括号中最后一个参数是函数体,之前所有的参数都是形参.

var sayFunc=new Function("name","age","alert(name+'今年'+age+'岁了')");
// sayFunc("李四",4);
alert("sayFunc方法对象的方法参数个数:"+sayFunc.length);
alert(sayFunc.toString());//获取源码
alert(sayFunc.valueOf());//获取源码

深入学习jquery源码之创建科学、复用率高的对象

深入学习jquery源码之创建科学、复用率高的对象

深入学习jquery源码之创建科学、复用率高的对象

深入学习jquery源码之创建科学、复用率高的对象

 

 

函数劫持

函数劫持:改变javascript的预定义的函数预定义好的功能

window.alert = function(x){
document.write(x) ;
}
alert("abc") ;

   a.如果两个函数的命名相同,后面的将会覆盖前面的函数。
   b.以基本语法声明的函数,会在代码运行的时候,提前加载到内存当中,以供以后使用,
     但是匿名函数形式命名的函数,会在执行到的时候,才进行赋值

   c.在不同的<script></script>块中的函数,使用和调用的时候,应该先定义,后执行。

 

通过工厂模式创建对象

工厂模式虽然解决多次创建相似对象的重复性问题,但是并没有解决对象识别问题,也就是typeof之后他都显示object,具体的对象是什么并没有显示

function createPerson(name,age,job)
{
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
alert(this.name);//this指的是o
}
return o;
}
var person1=createPerson("Tom",23,"厨师");
person1.sayName();

 

创建科学、复用率高的对象

构造函数模式和工厂模式的区别

  1.没有显式的创建对象。

  2.将属性和方法赋给了this对象。

  3.没有return语句。

  4.函数名第一个字母大写。

构造函数模式优于工厂模式的原因就是,构造函数模式中的对象实例(person1)通过constructor属性或instanceof操作符可以验证person1既是Object的实例,也是Person的实例,同时也证明所有对象均来自于Object。

function person(name,age){
this.name2=name; //那个对象引用this就代表那个对象
this.age2=age; //通过this关键字设置默认成员
var worker = 'coding'; //没有this关键字,对象创建后,该变量为非成员只能在方法内部使用
function speak(something){
alert(something);
}

this.func=speak;
}

var p1=new person("Jack",12);
alert(p1.name2);
p1.func("Hello,EveryOne!");

深入学习jquery源码之创建科学、复用率高的对象

深入学习jquery源码之创建科学、复用率高的对象

function C(){
var privateFunc=function(){
alert("私有方法");
};
privateFunc();
this.objFunc=function(){
alert("对象方法");
};
C.prototype.objFunc2=function(){
alert("对象方法2");
};
}
C.classFunc=function(){
alert("类方法");
};

 

定义对象模拟数组

function myArray () {
var lengs= arguments.length;
for (var i=0; i<lengs; i++) {
this[i]=arguments[i];
}
}
var arr=new myArray(1,2,3);
alert(arr[0]);

构造函数也有缺点,对象是引用类型,对象实例化不是指针的改变,而是简单的复制,复制对象的方法和属性,假设一个对象有上千个实例,它就会复制上千个功能相同的方法,这显然是不可取的。

function dianshi (color,size,brand) {
var Tv={};
Tv.color=color;
Tv.size=size;
Tv.brand=brand;
Tv.look=function () {
alert("看电视");
}
Tv.play=function () {
alert("玩游戏");
}
Tv.dvd=function () {
alert("DVD");
}
return Tv;
}
var ds=dianshi("red","30inch","sony");
//alert(typeof ds)//返回object
alert(ds.color)
var ds1=dianshi("blue","40inch","changh");
alert(ds1["size"])//传递参数

   我们也可以把sayName()函数的定义战役到构造函数的外部,这样我们就将sayName属性设置成等于全局的sayName函数,这样实例化对象就共享全局作用域中的同一个sayName(),解决了构造函数对象方法的多次创建问题。但是全局作用域定义的sayName()函数只能被某个对象调用谈什么全局作用域,而且如果构造函数对象的方法有很多,就需要定义很多全局函数,封装性又从何谈起,于是原型模式应运而生。

 

原型模式创建对象

我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象中包含着所有对象实例的属性和方法,这个对象就是原型对象。通俗的说,原型对象中的方法和属性可以被所有对象的实例所共享,对象实例就不用多次创建相同的方法和属性。

function Person(){

};
Person.prototype={
name:"Tom",
age:23,
job:"web前端工程师",
sayName:function(){
alert(this.name);
}
}
var person1=new Person();
person1.sayName();

深入学习jquery源码之创建科学、复用率高的对象

定义对象中的funciton

// 设置一个允许标记
var allowDemoRun = true;
var oldDemo = $.prototype.demo;
// 重写jQuery类的demo方法
$.prototype.demo = function(){
// 假如标记为false就不允许执行
if(!allowDemoRun) return;
// 调用jQuery类原来的demo方法
oldDemo&&oldDemo.apply(this,arguments);
}
$("p").demo();
buttonA.click(function() {

allowDemoRun = false;

});
buttonB.click(function() {
allowDemoRun = true;
});

原型链调用function

;define(function (require, exports, module) {
var troubleMaker= {
abC: function(plz) {
this.abC = plz;
return this;
},
deF: function(zlp) {
this.deF = zlp;
return this;
}
};
module.exports = eventTracker;
});

在被引用的模块内第一次有效,第二次就会提示abC is not a function。问题原因:abC方法名和方法内部的this.abC重名了。

解决:

;define(function (require, exports, module) {
var troubleMaker= {
abC: function(plz) {
this._abC = plz;
return this;
},
deF: function(zlp) {
this._deF = zlp;
return this;
}
};
module.exports = eventTracker;
});

共同的属性与方法放在代码段,节省内存空间

function Tv(color,size,brand) {
this.color=color;
this.size=size;
this.brand=brand;
this.play=function () {
alert("玩游戏");
}
}

Tv.prototype.look=function () {
alert("看电视");
}
Tv.prototype.dvd=function () {
alert("DVD");
}
Tv.prototype.aaa={name:"张三"};//只能共享属性或函数,不能共享对象
var sony=new Tv("red","20 inch","sony");
var changhong =new Tv("red","20 inch","CH");

delete sony.color
delete sony.play//undefine
delete sony.look//能访问到

sony.look();
changhong.look();

sony.aaa.name="李四"//李四
changhong.aaa.name//李四

 

在处理构造函数的时候,可以通过let 绑定来共享一个或者多个私有成员,而不使用闭包

var Thing;
{
let privateScope = new WeakMap();
let counter = 0;

Thing = function(){
this.someProperty = 'foo';
privateScope.set(this, {
hidden: ++counter,
});
};

Thing.prototype.showPublic = function(){
return this.someProperty;
};

Thing.prototype.showPrivate = function(){
return privateScope.get(this).hidden;
};
}

console.log(typeof privateScope); //undefined

var thing = new Thing();
console.log(thing); //Thing { someProperty: 'foo' }
console.log(thing.showPublic()); //foo
console.log(thing.showPrivate()); //1

 

全局jquery对象的创建

(function(){
var jQuery = function(){
return new F();
};
var F = function(){
return this;
};
F.fn = F.prototype = {
jquery : 1.0,
test : function(){
console.log('test');
return this;
}
};
jQuery.fn = jQuery.prototype = F.prototype;
window.jQuery = window.$ = jQuery;
})();

var a = $('body').test('li');
console.log('');
$.fn.extend = function(){console.log('extended');}
a.extend();

虽然可以通过对象实例访问保存在原型对象中的值,但却不能通过对象实例重写原型的值。其实对象实例获取某一属性的值是从本身开始寻找,然后是原型对象,最后是构造函数对象,所以重写对象实例的属性值(这个值可以通过delete操作符删除)仅仅是阻断了获取原型属性值的途径,但是没有改变其中的值。

 

引用类型存在的问题

function Obj(){
this.a=[]; //实例变量
this.fn=function(){ //实例方法

}
}
var o1=new Obj();
o1.a.push(1);
o1.fn={};
console.log(o1.a); //[1]
console.log(typeof o1.fn); //object
var o2=new Obj();
console.log(o2.a); //[]
console.log(typeof o2.fn); //function

在o1中修改了a和fn,而在o2中没有改变,由于数组和函数都是对象,是引用类型, 这就说明o1中的属性和方法与o2中的属性与方法虽然同名但却不是一个引用,而是对Obj对象定义的属性和方法的一个复制。 这个对属性来说没有什么问题,但是对于方法来说问题就很大了,因为方法都是在做完全一样的功能,但是却又两份复制,如果一个函数对象有上千和实例方法, 那么它的每个实例都要保持一份上千个方法的复制,这显然是不科学的

function Person(name){
this.name=name;
}

Person.prototype.printName=function(){
alert(this.name);
}

var person1=new Person('Byron');
var person2=new Person('Frank');

Person的实例person1中包含了name属性,同时自动生成一个__proto__属性,该属性指向Person的prototype,可以访问到prototype内定义的printName方法,大概就是这个样子的

深入学习jquery源码之创建科学、复用率高的对象

根据上图可以看出Person对象会自动获得prototyp属性,而prototype也是一个对象,会自动获得一个constructor属性,该属性正是指向Person对象。 当调用构造函数创建一个实例的时候,实例内部将包含一个内部指针(很多浏览器这个指针名字为__proto__)指向构造函数的prototype,这个连接存在于实例和构造函数的prototype之间, 而不是实例与构造函数之间

 

构建一个复用率高的对象

实例对象的属性或函数则定义到prototype中, 如果希望每个实例单独拥有的属性或方法则定义到this中,可以通过构造函数传递实例化参数。

function Person(name){
this.name=name;
}

Person.prototype.share=[];

Person.prototype.printName=function(){
alert(this.name);
}

 

构建一个类,new来创建对象

var jQuery = function () {
// 构造函数
}

jQuery.prototype = {
version: '1.1.1',
name: function() {
console.log(this.version);
}
}

var a = new jQuery();

a.name(); // "1.1.1"

 

构造函数创建的属性是实例私有的,原型创建的属性和方法是实例共享的。

function SuperType(name,colors)
{
this.name=name;
this.colors=colors;
};
SuperType.prototype.sayName=function()
{
alert(this.name);
};
function SubType(age,name,colors)
{
SuperType.call(this,name,colors);
this.age=age;
};
SubType.prototype=new SuperType();
SubType.prototype.sayAge=function()
{
alert(this.age);
};
var person1=new SubType(23,"Tom",["blue","red","green"]);
document.writeln(person1.colors);//来自父对象构造函数
document.writeln(person1.name);//来自父对象构造函数
person1.sayName();//来自父原型对象
document.writeln(person1.age);//来自子对象构造函数
person1.sayAge();//来自子原型对象

prototype内属性、方法是能够共享

function Person(name){
this.name=name;
}

Person.prototype.share=[];

Person.prototype.printName=function(){
alert(this.name);
}
var person1=new Person('Byron');
var person2=new Person('Frank');

person1.share.push(1);
person2.share.push(2);
console.log(person2.share); //[1,2]

 

借助原型可以基于已有的对象创建新对象,同时还不用创建自定义类型方法来达到这个目的

function object (o)//这个o相当于父对象实例
{
function F(){}//这个F相当子对象
F.prototype=o;//继承
return new F();//实例化传出
}
function inheritPrototype(subType,superType)
{
var prototype=object(superType.prototype);//创建对象
prototype.construct=subType;//增强对象
subType.prototype=prototype;//指定对象
}
function SuperType(name)
{
this.name=name;
}
SuperType.prototype.sayName=function()
{
alert(this.name);
}
function SubType(name,age)
{
SuperType.call(this,name);
this.age=age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge=function(){
alert(this.age);
}
var person1=new SubType("Tom",23);
   person1.sayName();

 

属性方法覆盖

实际上当代码读取某个对象的某个属性的时候,都会执行一遍搜索,目标是具有给定名字的属性,搜索首先从对象实例开始,如果在实例中找到该属性则返回, 如果没有则查找prototype,如果还是没有找到则继续递归prototype的prototype对象,直到找到为止,如果递归到object仍然没有则返回错误。 同样道理如果在实例中定义如prototype同名的属性或函数,则会覆盖prototype的属性或函数。

function Person(name){
this.name=name;
}

Person.prototype.share=[];
var person=new Person('Byron');
person.share=0;

console.log(person.share); //0而不是prototype中的[]

 

constructor

在 Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用。 
在定义一个函数时,JavaScript 内部会执行如下几个动作:

1.为该函数添加一个原形(即 prototype)属性 
2. 为 prototype 对象额外添加一个 constructor 属性,并且该属性保存指向函数F 的一个引用

这样当我们把函数 F 作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(这里就是我们的自定义构造函数 F)的 prototype 对象的一个属性proto,

所以我们在每一个对象实例中就可以访问构造函数的 prototype 所有拥有的全部属性和方法,就好像它们是实例自己的一样。当然该实例也有一个 constructor属性了(从 prototype 那里获得的),每一个对象实例都可以通过 constrcutor 对象访问它的构造函数。

面向对象创建对象

原型模式的缺点:因为所以对象实例共享原型对象的方法和属性,但是往往实例都有他自己私有的属性,这时候原型模式就不适用了,所以我们可以混合使用构造函数模式和原型模式。

无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,默认情况下prototype属性会默认获得一个constructor(构造函数)属性, 这个属性是一个指向prototype属性所在函数的指针

function Person(){

}

深入学习jquery源码之创建科学、复用率高的对象

用面向对象语法表示的时候,原型对象的constructor属性不在指向Person,因为每创建一个函数,同时会创建它的prototype对象,用面向对象语法本质上相当于重写了prototype对象,constructor属性也会变成新对象的constructor属性(这里指向Object)

function Person(){

};
Person.prototype={
constructor:Person,
name:"Tom",
age:23,
job:"厨师",
sayName:function(){
alert(this.name);
}
}
var person1=new Person();
var person2=new Person();
person1.name="Mike";
alert(person1.name);
alert(person2.name);

对象类型判断

var f = new F();
if(f.constructor === F) {
// do sth with F
}

其实 constructor 的出现原本就是用来进行对象类型判断的,但是 constructor 属性易变,不可信赖。我们有一种更加安全可靠的判定方法:instanceof 操作符。

if(f instanceof F) {
// do sth with F
}

 

原型链继承

一个子类对象可以获得其父类的所有属性和方法,称之为继承。

由于 constructor 存在于 prototype 对象上,因此我们可以结合 constructor 沿着原型链找到最原始的构造函数,如下面代码:

function Base() {}

// Sub1 inherited from Base through prototype chain
function Sub1(){}
Sub1.prototype = new Base();
Sub1.prototype.constructor = Sub1;

Sub1.superclass = Base.prototype;

// Sub2 inherited from Sub1 through prototype chain
function Sub2(){}
Sub2.prototype = new Sub1();
Sub2.prototype.constructor = Sub2;

Sub2.superclass = Sub1.prototype;

// Test prototype chain
alert(Sub2.prototype.constructor);// function Sub2(){}
alert(Sub2.superclass.constructor);// function Sub1(){}
alert(Sub2.superclass.constructor.superclass.constructor);// function Base(){}

 

constructor 易变,那是因为函数的 prototype 属性容易被更改,我们用时下很流行的编码方式来说明问题

function F() {}
F.prototype = {
_name: 'Eric',
getName: function() {
return this._name;
}
};

初看这种方式并无问题,但是你会发现下面的代码失效了:
var f = new F();
alert(f.constructor === F); // output false

F 不是实例对象 f 的构造函数了吗?当然是!只不过构造函数 F 的原型被开发者重写了,这种方式将原有的 prototype 对象用一个对象的字面量{}来代替。而新建的对象{}只是 Object 的一个实例,系统(或者说浏览器)在解析的时候并不会在{}上自动添加一个 constructor 属性,因为这是 function 创建时的专属操作,仅当你声明函数的时候解析器才会做此动作。然而你会发现 constructor 并不是不存在的。

既然存在,那这个 constructor 是从哪儿冒出来的呢?我们要回头分析这个对象字面量 {}。因为{}是创建对象的一种简写,所以{}相当于是 new Object()。那既然{}是 Object 的实例,自然而然他获得一个指向构造函数 Object()的 prototype 属性的一个引用proto,又因为 Object.prototype 上有一个指向 Object 本身的 constructor属性。所以可以看出这个constructor其实就是Object.prototype的constructo
一个解决办法就是手动恢复他的 constructor,下面代码非常好地解决了这个问题:

function F() {}
F.prototype = {
constructor: F, /* reset constructor */
_name: 'Eric',
getName: function() {
return this._name;
}
};
var f = new F();
alert(f.constructor === F); // output true this time ^^

 

构造函数上怎么还有一个 constructor ?它又是哪儿来的?细心的会发现,像 JavaScript 内建的构造函数,如 Array, RegExp, String,Number, Object, Function 等等居然自己也有一个 constructor: 

经过测试发现,此物非彼物它和 prototype 上 constructor 不是同一个对象,他们是共存的:

alert(typeof Array.constructor != 'undefined');// output true
alert(typeof Array.prototype.constructor === Array); // output true

在JavaScript中,每个具有原型的对象都会自动获得constructor属性。除了​​arguments​​​、Enumerator、Error、​​Global​​​、Math、​​RegExp​​​、Regular Expression等一些特殊对象之外,其他所有的JavaScript内置对象都具备constructor属性。例如:Array、​​Boolean​​、Date、Function、Number、Object、String等。所有主流浏览器均支持该属性

不过这件事情也是好理解的,因为 构造函数也是函数。是函数说明它就是 Function 构造函数的实例对象,自然他内部也有一个指向 Function.prototype 的内部引用proto啦。因此我们很容易得出结论,这个 constructor(构造函数上的constructor 不是 prototype 上的)其实就是 Function 构造函数的引用:

alert(Array.constructor === Function);// output true
alert(Function.constructor === Function); // output true

构造函数(Constructor)在对象创建或者实例化时候被调用的方法。通常使用该方法来初始化​​数据成员​​​和所需资源。​​构造器​​​Constructor在js不能被继承,因此不能重写​​Overriding​​​,但可以被​​重载​​​​Overloading​

 

在一个构造方法中可以使用​​super​​关键字来调用一个父类的构造方法。

如果没有显式指定构造方法,则会添加默认的 constructor 方法。

如果不指定一个构造函数(constructor)方法, 则使用一个默认的构造函数(constructor)。

class Polygon {
// ..and an (optional) custom class constructor. If one is
// not supplied, a default constructor is used instead:
// constructor() { }
constructor(height, width) {
this.name = 'Polygon';
this.height = height;
this.width = width;
}

// Simple class instance methods using short-hand method
// declaration
sayName() {
ChromeSamples.log('Hi, I am a ', this.name + '.');
}

sayHistory() {
ChromeSamples.log('"Polygon" is derived from the Greek polus (many) ' +
'and gonia (angle).');
}

// We will look at static and subclassed methods shortly
}

// Classes are used just like ES5 constructor functions:
let p = new Polygon(300, 400);
p.sayName();
ChromeSamples.log('The width of this polygon is ' + p.width);

调用

class Square extends Polygon {
constructor(length) {
// 在这里, 它调用了父类的构造函数, 并将 lengths 提供给 Polygon 的"width"和"height"
super(length, length);
// 注意: 在派生类中, 必须先调用 super() 才能使用 "this"。
// 忽略这个,将会导致一个引用错误。
this.name = 'Square';
}
get area() {
return this.height * this.width;
}
set area(value) {
// 注意:不可使用 this.area = value
// 否则会导致循环call setter方法导致爆栈
this._area = value;
}
}

let s = new Square(5);

s.sayName();
ChromeSamples.log('The area of this square is ' + s.area);

 

js对象的constructor属性返回创建该对象的函数的引用。

// 字符串:String()
var str = "张三";
document.writeln(str.constructor); // function String() { [native code] }
document.writeln(str.constructor === String); // true
// 数组:Array()
var arr = [1, 2, 3];
document.writeln(arr.constructor); // function Array() { [native code] }
document.writeln(arr.constructor === Array); // true
// 数字:Number()
var num = 5;
document.writeln(num.constructor); // function Number() { [native code] }
document.writeln(num.constructor === Number); // true
// 自定义对象:Person()
function Person(){
this.name = "CodePlayer";
}
var p = new Person();
document.writeln(p.constructor); // function Person(){ this.name = "CodePlayer"; }
document.writeln(p.constructor === Person); // true
// JSON对象:Object()
var o = { "name" : "张三"};
document.writeln(o.constructor); // function Object() { [native code] }
document.writeln(o.constructor === Object); // true
// 自定义函数:Function()
function foo(){
alert("CodePlayer");
}
document.writeln(foo.constructor); // function Function() { [native code] }
document.writeln(foo.constructor === Function); // true
// 函数的原型:bar()
function bar(){
alert("CodePlayer");
}
document.writeln(bar.prototype.constructor); // function bar(){ alert("CodePlayer"); }
document.writeln(bar.prototype.constructor === bar); // true

 

构造函数定义实例的私有属性,原型模式定义共享属性和方法。

function Tv(color,size,brand) {
this.color=color;
this.size=size;
this.brand=brand;
this.play=function () {
alert("玩游戏");
}

Tv.prototype.aaa={name:"张三"};

}

Tv.prototype.look=function () {
alert("看电视");
}
Tv.prototype.dvd=function () {
alert("DVD");
}
}