《Javascript深层原理探讨进阶版本.docx》由会员分享,可在线阅读,更多相关《Javascript深层原理探讨进阶版本.docx(29页珍藏版)》请在冰豆网上搜索。
变量num,bol,str为基本数据类型,它们的值,直接存放在栈中,obj,person,arr为复合数据类型,他们的引用变量存储在栈中,指向于存储在堆中的实际对象。
Typeof运算符无法区分这两种值,因此对于变量是否存在的判断操作是通过if(typeofvar==‘undefined’){//codehere}来进行判断的,这样既完全兼容未定义(undefined)和未初始化(uninitialized)两种情况的。
这种tag,还包括属性,注释,文本之类)都看作为不同的节点。
DOMCORE在解析文档时,会将所有的元素、属性、文本、注释等等视为一个节点对象(或继承自节点对象的对象,多态、向上转型),根据文本结构依次展现,最后行成了一棵"DOM树"
DOMHTML的核心概念是HTMLElement,DOMHTML会将文档中的元素(这里的元素特指
这种tag,不包括注释,属性,文本)都视为HTMLElement。
而元素的属性,则为HTMLElement的属性。
其实上DOMCore和DOMhtml的外部调用接口相差并不是很大,对于html文档可以用DOMhtml进行操作,针对Xhtml可以用DOMCore。
DOM模型示例:
关于BOM
BOM解析:
1.BOM是browserobjectmodel的缩写,简称浏览器对象模型
2.BOM提供了独立于内容而与浏览器窗口进行交互的对象
3.由于BOM主要用于管理窗口与窗口之间的通讯,因此其核心对象是window
4.BOM由一系列相关的对象构成,并且每个对象都提供了很多方法与属性
BOM模型示例:
基本的数据类型
"基本的数据类型"与"基本数据类型"的概念不一样:
1."基本的数据类型"指的是最常用的数据类型
2."基本数据类型"指的是原始类型(储存在内存中的方式)
(一)原始类型(简单数据类型、基本数据类型)
Undefined类型:
表示声明了变量但未对其初始化时赋予该变量的值。
undefined为Undefined类型下的唯一的一个值。
Null类型:
用于表示尚未存在的对象。
Null类型下也只有一个专用值null。
Boolean类型:
有两个值true和false,主要用于条件判断,控制执行流程。
Number类型:
代表数字(即包括32的整数,也包括64位的浮点数)
String类型:
用于代表字符串。
(二)对象
一个无序属性的集合,这些属性的值为简单数据类型、对象或者函数。
这里对象并不特指全局对象Object.
(三)函数
函数是对象的一种,实现上内部属性[[Class]]值为"Function",表明它是函数类型。
除了对象的内部属性方法外,还有[[Construct]]、[[Call]]、[[Scope]]等内部属性。
函数作为函数调用与构造器(使用new关键字创建实例对象)的处理机制不一样(Function对象除外)。
内部方法[[Construct]]用于实现作为构造器的逻辑,方法[[Call]]实现作为函数调用的逻辑。
这里的函数并不特指全局对象Function。
内置数据类型(内置对象)
Function:
函数类型的用户接口。
Object:
对象类型的用户接口。
Boolean,Number,String:
分别为这三种简单数值类型的对象包装器,对象包装在概念上有点类似C#/Java中的Box/Unbox。
Date,Array,RegExp:
可以把它们看作是几种内置的扩展数据类型。
注:
1.它们都是JavaScript语言的内置对象,都可以看作是函数的派生类型,在这个意义上,可将它们跟用户自定义的函数等同看待。
2.它们各自可以代表一种数据类型,是暴露给开发者对这些内置数据类型进行操作的接口。
在这个意义上,它们都是一种抽象的概念,后面隐藏了具体的实现机制。
数据类型实现模型描述
标准注解:
Build-in***datastructure:
指JS内部用于实现***类型的数据结构,由宿主环境(浏览器)提供,这些结构我们基本上无法直接操作。
Build-in***object:
指JS内置的Number,String,Boolean等这些对象,这是JS将内部实现的数据类型暴露给开发者使用的接口。
Build-in***constructor:
指JS内置的一些构造器,用来构造相应类型的对象实例。
它们被包装成函数对象暴露出来
可理解:
datastructure:
存储在内存中的数据
object:
对于存储在内存中的数据的一种包装(也存放在内存中),提供各种接口以供程序语言对存储在内存中的数据进行操作。
constructor:
将存储在内存中的数据包装的方法。
关于简单数据类型的对象化
一个细微的地方,下面描述对于Boolean,String和Number这三种简单数值类型都适用,以Number为例说明。
JS规范要求:
使用varnum1=123;这样的代码,直接返回基本数据类型,就是说返回的对象不是派生自Number和Object类型,用num1instanceofObject测试为false;
使用new关键字创建则返回Number类型,例如varnum2=newNumber(123);num2instanceofNumber为true。
将Number当作函数调用,返回结果会转换成简单数值类型。
示例代码:
varnum1=newNumber(123);
num1instanceofNumber//result:
true
num1instanceofObject//result:
true
num1=Number(num1);
num1instanceofNumber//result:
false
num1instanceofObject//result:
false
varnum2=123;
num2instanceofNumber//result:
false
num2instanceofObject//result:
false
结论:
虽然我们得到了一个简单数值类型,但它看起来仍然是一个JSObject对象,具有Object以及相应类型的所有属性和方法,使用上基本没有差别,唯一不同之处是instanceof的测试结果。
由此也就产生了一个概念"LiteralSyntax"
LiteralSyntax-字面量定义方法
可以理解为在定义一个变量的同时初始化赋值。
1、简单数据类型:
数值:
vari=100;//替代vari=newNumber(100);
布尔值:
varb=true; //替代varb=newBoolean(true);
字符:
varstr='thisisastring.';//替代varstr=newString('thisisastring');
2、复合数据类型
数组:
vararr=['笨蛋的座右铭',25]
对象:
varobj ={name:
'笨蛋的座右铭',age:
25}
正则表达式:
varreg=/\d+/;
Json:
varobj={name:
’测试’,
age:
25,
Type:
[‘测试’,’25’,[‘c1’,’c2’,’c3’,’c4’],function(){//code},{name:
'笨蛋的座右铭',age:
25}],
getname:
function(){//code}
}
原型继承原理
什么是prototype?
JavaScript中对象的prototype属性,可以返回对象类型原型的引用。
在面向对象领域里,实例与类型不是唯一的一对可描述的抽象关系,在JavaScript中,另外一种重要的抽象关系是类型(Type)与原型(prototype)。
这种关系是一种更高层次的抽象关系,它恰好和类型与实例的抽象关系构成了一个三层的链。
实例、类型、原型实例:
//Animal对象类型
functionAnimal(name){
this.name=name;
}
//Animal对象原型对象
Animal.prototype={
id:
"Animal",
sleep:
function(){
alert("sleep");
}
}
//Animal实例
Vardog=newAnimal();
其对应的简易内存分配结构图:
prototype与[[prototype]]
[[prototype]]与prototype并不是同一个东西:
1.每个函数对象都有一个显示的prototype属性,它代表了对象的原型,更明确的说是代表了由函数对象(构造函数)所创建出来对象原型。
2.每个对象都有一个名为[[Prototype]]的内部属性,指向于它所对应的原型对象。
对象的原型对象必然也有[[prototype]]属性指向于它所对应的原型对象,由此便构成了一种链表的结构,这就是原型链的概念。
3.JavaScript对象应当都通过prototype链关联起来,最顶层是Object,即对象都派生自Object类型。
4.prototype链的根为Object.prototype,对象Object.prototype的内部[[prototype]]属性为null.
constructor实现原理
constructor指的就是对象的构造函数。
在JavaScript中,每个函数都有名为“prototype”的属性,用于引用原型对象。
此原型对象又有名为“constructor”的属性,它反过来引用函数本身。
这是一种循环引用。
constructor是Function在创建函数对象时产生的,它是函数对象prototype链中的一个属性。
constructor的原理就是在对象的原型链上寻找constructor属性。
示例代码:
functionAnimal(){}
functionPerson(){}
Person.prototype=newAnimal();
varperson=newPerson();
alert(person.constructor);//Animal
由于将Person.prototype指向了newAnimal。
此时,Person的prototype指向的是Animal的实例,所以person的constructor为Animal这个构造函数。
instanceof原理
instanceof可以判断一个对象是不是某个类或其子类的实例。
instanceof检测一个对象A是不是另一个对象B的实例的原理是:
查看对象B的prototype指向的对象是否在对象A的[[prototype]]链上。
如果在,则返回true,如果不在则返回false。
不过有一个特殊的情况,当对象B的prototype为null将会报错(类似于空指针异常)。
函数对象的创建过程
函数就是对象,代表函数的对象就是函数对象。
函数对象是由Function这个函数对象构造出来的。
Function对象本身也是一个函数,因此它也一个函数对象。
函数对象详细创建步骤如下:
1.创建一个build-inobject对象fn
2.将fn的内部[[Prototype]]设为Function.prototype
3.设置内部的[[Call]]属性,它是内部实现的一个方法,处理函数调用的逻辑。
4.设置内部的[[Construct]]属性,它是内部实现的一个方法,处理逻辑参考对象创建过程。
5.设置fn.length为funArgs.length,如果函数没有参数,则将fn.length设置为0
6.使用newObject()同样的逻辑创建一个Object对象fnProto
7.将fnProto.constructor设为fn
8.将fn.prototype设为fnProto
9.返回fn
步骤1跟步骤6的区别为,步骤1只是创建内部用来实现Object对象的数据结构(build-inobjectstructure),并完成内部必要的初始化工作,但它的[[Prototype]]、[[Call]]、[[Construct]]等属性应当为null或者内部初始化值,即我们可以理解为不指向任何对象(对[[Prototype]]这样的属性而言),或者不包含任何处理(对[[Call]]、[[Construct]]这样的方法而言)。
从上面的处理步骤可以了解,任何时候我们定义一个函数,它的prototype是一个Object实例,这样默认情况下我们创建自定义函数的实例对象时,它们的Prototype链将指向Object.prototype。
函数对象构造过程的分析
functionAnimal(){};和vardog=newAnimal();
上述两行简单的语句的实际构造过程可以等价于以下的代码:
functionAnimal(){};等价于:
{
Animal.prototype={constructor:
Animal};
}
vardog=newAnimal()等价于:
vardog=({
varo={};
o.{[[prototype]]=Animal.Prototype;
Animal.call(o);
Returno;
})();
Function与Object关系
Function与Object可以总结为下图:
蓝色线为类的constructor的实例
橙色线为类的实例
绿色线为类的prototype的实例
黑色线为类的constructor的prototype的实例
由上图可以得出下列结论:
1.Function和Object各为自身的实例。
2.Object是通过Function进行构造的,而Function则由自己构造自己。
3.Function的原型对象是Object的实例。
关于Fuction与Object的总结:
Function
函数就是对象,代表函数的对象就是函数对象。
所有的函数对象是被Function这个函数对象构造出来的。
也就是说,Function是最顶层的构造器。
它构造了系统中所有的对象,包括用户自定义对象,系统内置对象,甚至包括它自已。
这也表明Function具有自举性(自已构造自己的能力)。
这也间接决定了Function的[[call]]和[[constructor]]逻辑相同。
Object
对于Object它是最顶层的对象,所有的对象都将继承Object的原型,Object也是一个函数对象,Object是被Function构造出来的。
对象模型
JavaScript的对象模型如下图所示:
(红色虚线表示隐式Prototype链)
总结:
1.图中有好几个地方提到build-inFunctionconstructor,这是同一个对象,这说明了几个问题:
Function指向系统内置的函数构造器(build-inFunctionconstructor);Function具有自举性;系统中所有函数都是由Function构造。
2.左下角的obj1,obj2...objn范指用类似这样的代码创建的对象:
functionfn1(){};varobj1=newfn1();这些对象没有本地constructor方法,但它们将从Prototype链上得到一个继承的constructor方法,即fn.prototype.constructor,从函数对象的构造过程可以知道,它就是fn本身了。
3.右下角的obj1,obj2...objn范指用类似这样的代码创建的对象:
varobj1=newObject();或varobj1={};或varobj1=newNumber(123);或obj1=/\w+/;等等。
所以这些对象Prototype链的指向、从Prototype链继承而来的constructor的值(指它们的constructor是build-inNumberconstructor还是build-inObjectconstructor等)等依赖于具体的对象类型。
另外注意的是,varobj=newObject(123);这样创建的对象,它的类型仍然是Number,即同样需要根据参数值的类型来确定。
同样它们也没有本地constructor,而是从Prototype链上获得继承的constructor方法,即build-in***constructor,具体是哪个由数据类型确定。
4.Object.prototype是整个链的终结点,它的内部[[Prototype]]为null。
5.所有函数的Prototype链都指向Function.prototype。
这是规范要求的,因为设计者将Function设计为具有自举性。
6.Function.prototype的Prototype链指向Object.prototype,这也是规范强制要求的。
保证Prototype链只有唯一的一个终结点。
7.因为Function.prototype是一个函数对象,所以它应当具有显示的prototype属性,即Function.prototype.prototype,但只有FireFox中可以访问到。
8.用户自定义函数(userdefinedfunctions)默认情况下[[Prototype]]值是Object.prototype,即它的隐式Prototype链指向Object.prototype,所以图中就这样表示了,但并不代表总是这样,当用户设置了自定义函数的prototype属性之后,情况就不同了。
属性访问原则
使用obj.propName访问一个对象的属性时,按照下面的步骤进行处理(假设obj的内部[[Prototype]]属性名为__proto__):
1.如果obj存在propName属性,返回属性的值,否则
2.如果obj.__proto__为null,返回undefined,否则
3.返回obj.__proto__.propName
调用对象的方法跟访问属性搜索过程一样,因为方法的函数对象就是对象的一个属性值。
提示:
上面步骤中隐含了递归过程,步骤3中obj.__proto__是另外一个对象,同样将采用1,2,3这样的步骤来搜索propName属性。
这就是基于Prototype的继承和共享:
(object1的方法fn2来自object2,概念上即object2重写了object3的方法fn2。
)
本地属性与继承属性
看一下设置对象属性时的处理过程,obj.propName=value的赋值语句处理步骤如下:
1.如果propName的attribute设置为不能设值,则返回
2.如果obj.propName不存在,则为obj创建一个属性,名称为propName
3.将obj.propName的值设为value
可以看到,设值过程并不会考虑Prototype链,对象的属性无法修改其原型中的同名属性,而只会自身创建一个同名属性并为其赋值。
道理很明显,对象通过隐式Prototype链能够实现属性和方法的继承,但prototype也是一个普通对