《Javascript语言精粹》的学习(一).对象和函数
最近在学习小马和秦歌翻译的《javascript语言精粹》,果然如传闻般,里面的内容博大精深。有些章节所表达的意思往往需要看到后面的章节内容时才能够突然顿悟,因此,称这本书需要反复的去研究,真的名副其实。这两天看了对象和函数这两章,在这里把自己觉得需要关注的内容做一下笔记:
①对象常量中属性名的命名规范
记得刚开始学习对象常量的时候,觉得它真的很简便。一个对象名,一个大括号,N个“名/值”对,OK,搞定!可是在学习的过程中,也遇到过一些疑惑,比如在一些JS书籍中,在写对象常量时,对象中有的属性名用引号引起,有的属性名却没有引号,比如以下的对象常量:
var flight={ airline:"Oceannic", first_name:"li", “first-name”:"hu" }
这应该是比较熟悉的对象常量。可是为何first_name没有引号,而first-name却需要引号呢。以前看过的JS书籍都没有过解释,而我也习惯在写对象常量时,将所有的属性名都用引号括住。在本书中,终于给出了详细解释。在对象字面量中,如果属性名是一个合法的javascript标识符(标识符为首字母为单词,后面选择性加上一个或多个字母,数字或下划线组成)且不是保留字,那么属性名可以不加上引号,初次之外,属性名必须加上引号。本例汇总,first_name和airline是合法的标识符,而first-name不是标识符,所以必须加上引号。
②关于原型
我们都知道,JS的继承方式并不是传统的面向对象,而是面向原型继承的。因此,在JS中,每个对象都会连接到一个原型对象,并从中继承属性。而对象字面量连接到Object.prototype中。这里要注意的是,原型连接在更新时是不起作用的,只有在检索值的时候才会起作用。更新时不起作用就是说对象只能改变它自身包含的属性,而无法改变它连接到的原型链中的属性。检索时起作用是值当对象检索某个属性时,先检查自身有没有该属性,如果没有,再检索它连接到的原型对象,如果原型对象中也没有,再继续检索原型对象的原型对象,也就是顺着原型链查找。
③函数定义中形参的初始化
我们知道,函数的定义通常包括四部分,function,函数名(匿名函数可以省略),形参,花括号。但是JS引擎如何初始化形参的呢。我们知道,在解析函数时,JS一般分为两步:
第一是通过语法分析和预解析构建语法分析树
第二是执行函数,在执行每个函数时会为该函数创建具体的执行环境和活动对象。
具体可见我转载的一篇博文语法作用域与词法作用域。我这里要讲的是,普通的变量在构建语法分析树时就会被初始化,这时它们初始化为undefined(全局变量除外,全局变量直接初始化为它们所赋的值),而函数中的形参却只有在函数调用时才会被初始化为实际提供的参数值。
④蛋疼的this
我们知道,函数既可以当作一个对象的方法来调用,也可以独立调用。当它作为一个对象的方法时,显而易见,它指向的是调用它的对象。但是当它独立调用呢?给出一个例子:
var flight={ airline:"Oceanic", number:815, 'first-name':'hu', departure:{ IATA:"SYD", time:"2004-09-22 14:25", city:"Sydney" } } flight.double=function(){ var that=this; var helper=function(){ alert(that==this); } helper(); } flight.double();
首先我把外层函数的this赋给that,然后在内层函数中将this关键字与that做全等号比较,结果弹出一个打打的false!蛋疼,内层函数中this并没有绑定到外层函数的this,那么它绑定的是什么呢?好,修改一下代码:
flight.double=function(){ var that=this; var helper=function(){ alert(window==this); } helper(); } flight.double();
将this与window做全等,这时候再运行,发现弹出的是true。由此得出结论,当函数独立调用时(这里所属的独立是它既不属于对象的方法,也不是用new来调用),它绑定到的是全局对象。这时候,为了使用外层函数中的this,例子中已经演示,可以在外层函数作用域中定义一个that变量,然后将this的引用赋给它,在内层函数中,可以使用that来访问。
⑤伟大的闭包
习惯上,我们这样去定义一个对象常量:
var myObject={ value:0, increment:function(inc){ this.value+=typeof inc==='number'?inc:1; } }
这是一个很简单的定义。可是哪里似乎有些问题!!不错,安全性,某些时候,我值需要通过increment函数去修改value值,可是,如果这样定义的话,貌似value可以随便更改(myObject.value),这完全违反了我们的初衷,那么该如何修改呢?不错,利用闭包。
var myObject=function(){ var value=0; return{ increment=function(inc){ value+=typeof inc==='number'?inc:1' } getValue:function(){ return value; } } }();
大家再看,这时候,value的值可以如上例中随便更改呢,档案当然是否定的。函数作用域使它只对返回对象中的两个方法可用,对其它程序是不可用的。唉,而myObject的值也不是一个函数,而是匿名函数调用后返回的闭包。唉,闭包真的是伟大!
⑥级联
有用过jquery的童鞋应该知道,为何一个JQUERY对象可以在一条语句中调用那么多的方法呢。不错,用到的正是级联。那么级联的原理是什么呢?我们知道,有一些方法操作型的方法并没有任何返回值,这时候,这些方法返回的undefined,如果我们稍微设置一下,让这些不需要返回值的方法去返回this呢,也就是说在这些方法的末尾加上'return this'这么一条语句,不就实现级联了~嘿嘿,下面是我做的一个小demo,相信大家看明白就会理解级联了:
function $(e){ return new $.fn.init(e); } $.fn=$.prototype={ init:function(){ var elem=document.getElementById(arguments[0]); this[0]=elem; }, width:function(){ alert(this[0].style.width); return this; }, height:function(){ alert(this[0].style.height); return this; } }; $.fn.init.prototype=$.fn; $("dv").width().height();