Javascript深层原理探讨进阶版本.docx
- 文档编号:23694518
- 上传时间:2023-05-20
- 格式:DOCX
- 页数:29
- 大小:636.31KB
Javascript深层原理探讨进阶版本.docx
《Javascript深层原理探讨进阶版本.docx》由会员分享,可在线阅读,更多相关《Javascript深层原理探讨进阶版本.docx(29页珍藏版)》请在冰豆网上搜索。
Javascript深层原理探讨进阶版本
原始值和引用值
在ECMAScript中,变量可以存放两种类型的值,即原始值和引用值。
原始值指的就是代表原始数据类型(基本数据类型)的值,即Undefined,Null,Number,String,Boolean类型所表示的值。
引用值指的就是复合数据类型的值,即Object,Function,Array,以及自定义对象,等等
栈和堆
与原始值与引用值对应存在两种结构的内存即栈和堆
原始值是存储在栈中的简单数据段,也就是说,他们的值直接存储在变量访问的位置。
堆是存放数据的基于散列算法的数据结构,在JavaScript中,引用值是存放在堆中的。
变量num,bol,str为基本数据类型,它们的值,直接存放在栈中,obj,person,arr为复合数据类型,他们的引用变量存储在栈中,指向于存储在堆中的实际对象。
我们无法直接操纵堆中的数据,也就是说我们无法直接操纵对象,但我们可以通过栈中对对象的引用来操作对象。
堆比栈大,栈比堆的运算速度快,对象是一个复杂的结构,并且可以自由扩展,如:
数组可以无限扩充,对象可以自由添加属性。
将他们放在堆中是为了不影响栈的效率。
而是通过引用的方式查找到堆中的实际对象再进行操作。
相对于简单数据类型而言,简单数据类型就比较稳定,并且它只占据很小的内存。
不将简单数据类型放在堆是因为通过引用到堆中查找实际对象是要花费时间的,而这个综合成本远大于直接从栈中取得实际值的成本。
所以简单数据类型的值直接存放在栈中。
Null和Undefined的比较
在ECMAScript的原始类型中,是有Undefined和Null类型的。
这两种类型都分别对应了属于自己的唯一专用值,即undefined和null。
值undefined实际上是从值null派生来的,因此ECMAScript把它们定义为相等的。
尽管这两个值相等,但它们的含义不同。
undefined是声明了变量但未对其初始化时赋予该变量的值,null则用于表示尚未存在的对象。
Udefined代表没有赋值的基本数据类型,Null代表没有赋值的引用数据类型。
null参与数值运算时其值会自动转换为0,undefined参与任何数值计算时,其结果一定是NaN。
当声明的变量未初始化时,该变量的默认值是undefined,但是undefined并不同于未定义的值。
Typeof运算符无法区分这两种值,因此对于变量是否存在的判断操作是通过if(typeofvar==‘undefined’){//codehere}来进行判断的,这样既完全兼容未定义(undefined)和未初始化(uninitialized)两种情况的。
JavaScript全局观
JavaScript核心包括一下三部分:
1.核心(ECMAScript):
定义了脚本语言的所有对象,属性和方法。
2.文档对象模型(DOM):
HTML和XML应用程序接口。
3.浏览器对象模型(BOM):
对浏览器窗口进行访问操作。
关于ECMAScript
1.ECMAScript是一种由欧洲计算机制造商协会(ECMA)通过ECMA-262标准化的脚本程序设计语言。
2.ECMAScript的工作是定义语法和对象,从最基本的语法、数据类型、条件语句、关键字、保留字到异常处理和对象定义都是它的范畴。
3.JavaScript实现了ECMAScript,AdobeActionScript和OpenViewScriptEase同样也实现了ECMAScript
4.在ECMAScript范畴内定义的对象也叫做原生对象,如Object、Array、Function等等。
5.由ECMA-262定义的ECMAScript与Web浏览器没有依赖关系。
其实上它就是一套定义了语法规则的接口,然后由不同的浏览器对其进行实现,最后我们输写遵守语法规则的程序,完成应用开发需求。
关于DOM
文档对象模型(DocumentObjectModel)定义了访问和处理文档的标准方法。
根据DOM的定义(HTML和XML应用程序接口)可知DOM由两个部分组成:
1.针对XML的DOM即DOMCore
2.针对HTML的DOMHTML。
DOMCore的核心概念就是节点(Node)。
DOM会将文档中不同类型的元素(这里的元素并不特指
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也是一个普通对
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Javascript 深层 原理 探讨 进阶 版本
![提示](https://static.bdocx.com/images/bang_tan.gif)