javascript原理解析.docx
《javascript原理解析.docx》由会员分享,可在线阅读,更多相关《javascript原理解析.docx(20页珍藏版)》请在冰豆网上搜索。
javascript原理解析
1.Javasript引擎
1.1什么是JavaScript解析引擎?
简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序。
比方说,当你写了 vara=1+1; 这样一段代码,JavaScript引擎做的事情就是看懂(解析)你这段代码,并且将a的值变为2。
学过编译原理的人都知道,对于静态语言来说(如Java、C++、C),处理上述这些事情的叫编译器(Compiler),相应地对于JavaScript这样的动态语言则叫解释器(Interpreter)。
这两者的区别用一句话来概括就是:
编译器是将源代码编译为另外一种代码(比如机器码,或者字节码),而解释器是直接解析并将代码运行结果输出。
比方说,firebug的console就是一个JavaScript的解释器。
但是,现在很难去界定说,JavaScript引擎它到底算是个解释器还是个编译器,因为,比如像V8(Chrome的JS引擎),它其实为了提高JS的运行性能,在运行之前会先将JS编译为本地的机器码(nativemachinecode),然后再去执行机器码(这样速度就快很多),相信大家对JIT(JustInTimeCompilation)一定不陌生吧。
我个人认为,不需要过分去强调JavaScript解析引擎到底是什么,了解它究竟做了什么事情我个人认为就可以了。
对于编译器或者解释器究竟是如何看懂代码的,翻出大学编译课的教材就可以了。
这里还要强调的就是,JavaScript引擎本身也是程序,代码编写而成。
比如V8就是用C/C++写的。
1.2JavaScript解析引擎与ECMAScript是什么关系?
JavaScript引擎是一段程序,我们写的JavaScript代码也是程序,如何让程序去读懂程序呢?
这就需要定义规则。
比如,之前提到的vara=1+1;,它表示:
∙左边var代表了这是申明(declaration),它申明了a这个变量
∙右边的+表示要将1和1做加法
∙中间的等号表示了这是个赋值语句
∙最后的分号表示这句语句结束了
上述这些就是规则,有了它就等于有了衡量的标准,JavaScript引擎就可以根据这个标准去解析JavaScript代码了。
那么这里的ECMAScript就是定义了这些规则。
其中ECMAScript262这份文档,就是对JavaScript这门语言定义了一整套完整的标准。
其中包括:
∙var,if,else,break,continue等是JavaScript的关键词
∙abstract,int,long等是JavaScript保留词
∙怎么样算是数字、怎么样算是字符串等等
∙定义了操作符(+,-,>,<等)
∙定义了JavaScript的语法
∙定义了对表达式,语句等标准的处理算法,比如遇到==该如何处理
∙⋯⋯
标准的JavaScript引擎就会根据这套文档去实现,注意这里强调了标准,因为也有不按照标准来实现的,比如IE的JS引擎。
这也是为什么JavaScript会有兼容性的问题。
至于为什么IE的JS引擎不按照标准来实现,就要说到浏览器大战了,这里就不赘述了,自行Google之。
所以,简单的说,ECMAScript定义了语言的标准,JavaScript引擎根据它来实现,这就是两者的关系。
1.3JavaScript解析引擎与浏览器又是什么关系?
简单地说,JavaScript引擎是浏览器的组成部分之一。
因为浏览器还要做很多别的事情,比如解析页面、渲染页面、Cookie管理、历史记录等等。
那么,既然是组成部分,因此一般情况下JavaScript引擎都是浏览器开发商自行开发的。
比如:
IE9的Chakra、Firefox的TraceMonkey、Chrome的V8等等。
从而也看出,不同浏览器都采用了不同的JavaScript引擎。
因此,我们只能说要深入了解哪个JavaScript引擎。
2.Javascript中的__proto__,prototype和constructor
2.1__proto__
javascript中一切都是对象,每个对象都是基于原型对象创建的,每个对象中都有__proto__属性,这个属性指向的就是它基于的原型对象。
__proto__属性是系统(浏览器)自动给创建的,在对象被创建的时候自动指向基于的原型对象。
比如:
functionFoo(){
}
varfoo=newFoo();
foo.__proto__指向的就是Foo.prototype
因为Foo函数本身也是个对象,所以Foo.__proto__指向的是Function.prototype
Function.prototype本身也是对象,所以Function.prototype.__proto__指向的是Object.prototype
2.2prototype
只有构造函数对象才有prototype属性,构造函数的作用是创建对象,创建对象的时候,它要知道这个对象基于哪个原型来创建,这个prototype指向的就是这个原型。
prototype属性也是系统(浏览器)给创建的,并指向创建对象时基于的原型。
注意不是构造函数本身基于的原型,本身基于的原型都是Function.prototype。
比如:
functionFoo(){
}
//系统自动把Foo的prototype属性指向Foo.prototype
//所以,newFoo()的时候,就基于Foo.prototype来创建,
//foo.__proto__就是Foo.prototype
varfoo=newFoo();
foo.__proto__和Foo.prototype指向的都是Foo.prototype。
自定义一个Foo构造函数的时候,系统默认会自动指定一个Foo.prototype原型对象,其实就是一个Object对象。
2.3constructor
只有原型对象才有constructor属性,而且也是系统(浏览器)给创建的。
前面说过构造函数对象的prototype属性会指向一个原型对象,那么这个原型对象中的constructor属性指向的就是这个构造函数。
constructor属性其实是给自定义对象使用的,自定义对象通过constructor属性就可以知道自己是用哪个构造函数new出来的。
比如:
functionFoo(){
}
varfoo=newFoo();
alert(foo.constructor)//functionFoo(){}
//foo中没有constructor,foo访问的是它的原型Foo.prototype中的constructor属性。
一个对象被new出来以后,就可以知道自己的两方面事情:
1.通过foo.__proto__,可以知道自己是基于哪个原型创建出来的,相当于自己的父类
2.通过foo.constructor,可以知道自己是用哪个构造函数构造出来的相当于自己的类
2.4构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Emptyfunction)
Number.__proto__===Function.prototype//true
Boolean.__proto__===Function.prototype//true
String.__proto__===Function.prototype//true
Object.__proto__===Function.prototype//true
Function.__proto__===Function.prototype//true
Array.__proto__===Function.prototype//true
RegExp.__proto__===Function.prototype//true
Error.__proto__===Function.prototype//true
Date.__proto__===Function.prototype//true
JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。
剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。
它们的__proto__是Object.prototype。
如下
Math.__proto__===Object.prototype//true
JSON.__proto__===Object.prototype//true
上面说的“所有构造器/函数”当然包括自定义的。
如下
//函数声明
functionPerson(){}
//函数表达式
varMan=function(){}
console.log(Person.__proto__===Function.prototype)//true
console.log(Man.__proto__===Function.prototype)//true
这说明什么呢?
所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。
所有构造器都继承了Function.prototype的属性及方法。
如length、call、apply、bind(ES5)。
Function.prototype也是唯一一个typeofXXX.prototype为“function”的prototype。
其它的构造器的prototype都是一个对象。
如下
console.log(typeofFunction.prototype)//function
console.log(typeofObject.prototype)//object
console.log(typeofNumber.prototype)//object
console.log(typeofBoolean.prototype)//object
console.log(typeofString.prototype)//object
console.log(typeofArray.prototype)//object
console.log(typeofRegExp.prototype)//object
console.log(typeofError.prototype)//object
console.log(typeofDate.prototype)//object
console.log(typeofObject.prototype)//object
噢,上面还提到它是一个空的函数,alert(Function.prototype)下看看。
知道了所有构造器(含内置及自定义)的__proto__都是Function.prototype,那Function.prototype的__proto__是谁呢?
相信都听说过JavaScript中函数也是一等公民,那从哪能体现呢?
如下
Function.prototype.__proto__===Object.prototype//true
这说明所有的构造器也都是一个普通JS对象,可以给构造器添加/删除属性等。
同时它也继承了Object.prototype上的所有方法:
toString、valueOf、hasOwnProperty等。
最后Object.prototype的__proto__是谁?
Object.prototype.__proto__===null//true
已经到顶了,为null。
2.5对象的__proto__都指向其构造器的prototype
上面测试了所有内置构造器及自定义构造器的__proto__,下面再看看所有这些构造器的实例对象的__proto__指向谁?
先看看JavaScript引擎内置构造器
varobj={name:
'jack'}
vararr=[1,2,3]
varreg=/hello/g
vardate=newDate
varerr=newError('exception')
console.log(obj.__proto__===Object.prototype)//true
console.log(arr.__proto__===Array.prototype)//true
console.log(reg.__proto__===RegExp.prototype)//true
console.log(date.__proto__===Date.prototype)//true
console.log(err.__proto__===Error.prototype)//true
再看看自定义的构造器,这里定义了一个Person
functionPerson(name){
this.name=name
}
varp=newPerson('jack')
console.log(p.__proto__===Person.prototype)//true
p是Person的实例对象,p的内部原型总是指向其构造器Person的prototype。
每个对象都有一个constructor属性,可以获取它的构造器,因此以下打印结果也是恒等的
functionPerson(name){
this.name=name
}
varp=newPerson('jack')
console.log(p.__proto__===p.constructor.prototype)//true
上面的Person没有给其原型添加属性或方法,这里给其原型添加一个getName方法
functionPerson(name){
this.name=name
}
//修改原型
Person.prototype.getName=function(){}
varp=newPerson('jack')
console.log(p.__proto__===Person.prototype)//true
console.log(p.__proto__===p.constructor.prototype)//true
可以看到p.__proto__与Person.prototype,p.constructor.prototype都是恒等的,即都指向同一个对象。
如果换一种方式设置原型,结果就有些不同了
functionPerson(name){
this.name=name
}
//重写原型
Person.prototype={
getName:
function(){}
}
varp=newPerson('jack')
console.log(p.__proto__===Person.prototype)//true
console.log(p.__proto__===p.constructor.prototype)//false
这里直接重写了Person.prototype(注意:
上一个示例是修改原型)。
输出结果可以看出p.__proto__仍然指向的是Person.prototype,而不是p.constructor.prototype。
这也很好理解,给Person.prototype赋值的是一个对象直接量{getName:
function(){}},使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object,Object.prototype是一个空对象{},{}自然与{getName:
function(){}}不等。
如下
varp={}
console.log(Object.prototype)//为一个空的对象{}
console.log(p.constructor===Object)//对象直接量方式定义的对象其constructor为Object
console.log(p.constructor.prototype===Object.prototype)//为true,不解释
上面代码中用到的__proto__目前在IE6/7/8/9中都不支持。
IE9中可以使用Object.getPrototypeOf(ES5)获取对象的内部原型。
varp={}
var__proto__=Object.getPrototypeOf(p)
console.log(__proto__===Object.prototype)//true
3.JavaScript创建对象
通过JavaScript,您能够定义并创建自己的对象。
创建新对象有两种不同的方法:
1.定义并创建对象的实例
2.使用函数来定义对象,然后创建新的对象实例
3.1创建直接的实例
3.1.1对象直接量
varempty={};//Anobjectwithnoproperties
varpoint={x:
0,y:
0};
varcircle={x:
point.x,y:
point.y+1,radius:
2};
varhomer={
"name":
"HomerSimpson",
"age":
34,
"married":
true,
"occupation":
"plantoperator",
'email':
homer@
};
3.1.2通过new创建对象
vara=newArray();//Createanemptyarray
vard=newDate();//Createanobjectrepresentingthecurrentdateandtime
varr=newRegExp("javascript","i");//Createapattern-matchingobject
这个例子创建了对象的一个新实例,并向其添加了四个属性:
Person=newObject();
Person.firstname="Bill";
Person.lastname="Gates";
Person.age=56;
Person.eyecolor="blue";
3.2使用对象构造器
本例使用函数来构造对象:
functionPerson(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
}
3.2.1创建JavaScript对象实例
一旦您有了对象构造器,就可以创建新的对象实例,就像这样:
varmyFather=newPerson("Bill","Gates",56,"blue");
varmyMother=newPerson("Steve","Jobs",48,"green");
3.2.2把属性添加到JavaScript对象
您可以通过为对象赋值,向已有对象添加新属性:
假设PersonObj已存在-您可以为其添加这些新属性:
firstname、lastname、age以及eyecolor:
Person.firstname="Bill";
Person.lastname="Gates";
Person.age=56;
Person.eyecolor="blue";
x=Person.firstname;
在以上代码执行后,x的值将是:
Bill
3.2.3把方法添加到JavaScript对象
方法只不过是附加在对象上的函数。
在构造器函数内部定义对象的方法:
functionPerson(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
this.changeName=changeName;
functionchangeName(name)
{
this.lastname=name;
}
}
changeName()函数name的值赋给Person的lastname属性。
现在您可以试一下:
myMother.changeName("Ballmer");
3.3使用Object.create()
Object.create(prototype,descriptors):
创建一个具有指定原型且可选择性地包含指定属性的对象。
该函数是一个静态函数。
参数:
prototype必需。
要用作原型的对象。
可以为null。
descriptors可选。
包含一个或多个属性描述符的JavaScript对象。
“数据属性”是可获取且可设置值的属性。
数据属性描述符包含value特性,以及writable、enumerable和configurable特性。
如果未指定最后三个特性,则它们默认为false。
只要检索或设置该值,“访问器属性”就会调用用户提供的函数。
访问器属性描述符包含set特性和/或get特性。
varo1=Object.create(null);//o1不继承任何属性和方法
varo2=Object.create({x:
1,y:
2});//o2继承了属性x和y
varo3=Object.create(Object.prototype);//o3和newObject()一样
3.4JavaScript类
JavaScript是面向对象的语言,但JavaScript不使用类。
在JavaScript中,不会创建类,也不会通过类来创建对象(就像在其他面向对象的语言中那样)。
JavaScript基于prototype,而不是基于类的。
4.JavaScript事件监听
4.1事件监听的三种方式
4.1.1直接在代码上加载事件