http://hzjavaeyer.group.iteye.com/group/wiki?category_id=283
上面这个url非常适合javascript初学者阅读,感谢原作者的分享
什么是script?
A script is a series of instructions that a computer can follow to achieve a goal.
A browser may use different parts of the script depending on how the user interacts with the web page.
Scripts can run different sections of the code in response to the situation around them.
类似于菜谱,汽车维修操作说明书。
javascript中的类型
js分为两类:原始类型和(值引用)和Object类型(引用类型)。在浏览器宿主环境下,JS又可以分类为:原生对象,宿主对象,浏览器扩展对象(比如ActiveXObject).原始类型和对象类型他们的区别在于原始类型是值引用,分配在栈空间中,Object类型则在栈中分配的只是变量本身,它指向一个分配在堆中的内存块。
数据类型隐式转换:
- 当数字和字符串执行元算时," + " 操作将会把数字隐藏转换为字符串, " - "或者*/操作将会把字符串转换为数字
- 注意看下面的代码:3.1415这个数字如果放在()中并且使用“ . ”点操作符时,浏览器默认行为是将他转换为Number对象类型,而该Number对象类型是有一个toFixed()方法的;
(3.1415).toFixed(2)
"3.14"
console.dir(new Number(3.1415))
VM278:2 Number
同样的
"hello world".split(" ");
["hello", "world"]
上面这段代码执行时,浏览器将值直接量转换为String对象类型,故而可以调用String的split方法
- if(表达式)这里表达式也将隐式转换为布尔值;
- == 将隐式转==两边的值
显式类型转换方法
Number(), String(),Boolean(),parseInt(),parseFloat(), !, !!
typeof与instanceof的区别
typeof
operator returns a string indicating the type of the unevaluated operand.
typeof操作符能识别除了Null之外的标准原始类型,不能识别具体的对象类型(除了Function外)typeof(null)返回object
返回以下内容“ undefined, boolean, number, string, symbol, function, object"
typeof operand
instanceof
operator tests whether an object has in its prototype chain theprototype
property of a constructor
instanceof操作符可以识别所有的内置对象和自定义对象,但是不能识别原生初始类型的值
返回true或者false
object instanceof constructor
- Object.prototype.toString.call 可以识别除了自定义类型对象外的所有类型:
Object.prototype.toString.call(123); "[object Number]" Object.prototype.toString.call("this is a string"); "[object String]"
- constructor方法来识别类型
使用.constructor可以识别所有的类型(除了null,undefined外,当然即使是null,undefined,使用.constructor也可以返回他们本身!!)。其原理为:当传入原始类型时,点操作符使得js自动转换为对应的对象类型,built-in和自定义对象则会返回对象的构造函数本身。
function getConstructorName(obj){ return obj && obj.constructor && obj.constructor.toString().match(/function\s*([^(]*)/)[1]; } undefined getConstructorName(null) null getConstructorName(undefined) undefined getConstructorName([]) "Array" getConstructorName({}) "Object" getConstructorName(123) "Number" getConstructorName("string") "String"
constructor详解
上面通过使用obj.constructor.toString方法能够识别所有自定义对象的前提是:你须要预先执行下面的一行代码constructoFunction.prototype.constructor = constructorFunction;
当设置构造函数的prototype原型对象属性时,不能仅仅通过一条constructorFunction.prototype = { // 一个literal对象};这种方式来定义产出对象的原型,因为这么做的话,意味着constructorFunction.prototype.constructor = Object(因为obj.constructor永远指向创建自己的构造函数本身,而在这里constructorFunction.prototype对象实际上是由new Object来创造的,所以constructorFunction.prototype.constructor = Object!!)。
解决或避免上面问题的最佳实践是:每次设置构造函数的prototype属性时,顺带就执行constructorFunction.prototype.constructor = constructorFunction;
下面来解释一下相关背景。
1.每一个对象都有一个constructor属性包括构造函数本身,因为构造函数也是对象;
2.任何对象的constructor始终指向创建该对象的构造函数(只要检视等效的new XXXX,就知道对象的构造函数)
3.每一个函数都有一个默认的属性prototype,而这个prototype属性也是一个对象,他的constructor默认指向这个函数;
4.任何函数的prototype属性对象的constructor默认就是函数本身(在不显示设置prototype的情况下)
5.obj.constructor = objConstructor.prototype.constructor这是定律,由于prototype.constructor或显式或隐式被设置为构造函数本身,当然也可以设置为其他的函数
看看下面的代码:
function Person(name) { this.name = name; } var p = new Person("ZhangSan"); console.log(p.constructor === Person); // true p的构造函数为Person,因为p是通过new Person来创建的 console.log(Person.prototype.constructor === Person); // true 任何函数的prototype属性对象的constructor默认就是函数本身。 console.log(p.constructor.prototype.constructor === Person); // true
上面是在未设置Person的prototype属性对象的情况下默认的new Person行为;下面我们增加一行代码,即:指定Person构造函数的prototype属性,增加一个getName普遍方法:
function Person(name) { this.name = name; } Person.prototype = { //设置Person的prototype为一个literal对象,供后代继承 getName: function() { return this.name; } } var p = new Person("ZhangSan"); //由于任何对象的constructor指向创建自己的构造函数(new XXXX中的XXXX),实际由construcfunc.prototype.constructor或显式或默认来指定
console.log(p.constructor === Person); // false //obj.constructor = objConstructor.prototype.constructor这是定律 ,而这里Person.prototype的构造函数为Object, Object的prototype.constructor默认就指向Object构造函数本身,因此p.constructor === Object console.log(Person.prototype.constructor === Person); // false 实际上Person.prototype.constructor === Object console.log(p.constructor.prototype.constructor === Person); // false
解决上面的问题,只要在设置Person.prototype属性对象之后,添加一句: Person.prototype.constructor = Person即可!!
function:
function本身是一个object,它可以被赋值给任何变量,也可以有很多自定义的属性
immediate invoked function expression(IIFE): function(){}()
method vs function: 一个method是一个对象的一个函数属性(a method is a function that is a property of an object)例如:
var console={
log:function(){}
}
console.log(“log是console的method!");
String methods:
下面的方法或者属性是string对象定义的方法或者属性!
length: 这个length实际上是一个属性,而不是一个方法。
indexOf
var line = "HAL: I'm sorry, Dave. I'm afraid I can't do ▶ that"; alert( line.indexOf("I'm") ); // 5 alert( line.indexOf("I'm", 6) ); // 22
slice,substr,substring函数:
var greeting = "Hello, Andrew, what's up?", name = greeting.slice(7, 13); alert(name); // Andrew
split:该函数将一个字符串使用空格符作为分割依据将每个单词分开放到一个数组中
var arr = "apples oranges peaches bananas".split(" "); console.log(arr); // ["apples", "oranges", "peaches", ▶ "bananas"]
toLowerCase, toUpperCase
Date Methods
Date object有很多实用的方法可供使用
getDate,getDay,getFullYear,getHours,getMilliseconds,getMinutes,getMonth,getSeconds,getTime,getTimezoneOffset
setDate,setFullYear,setMinute,setMonth,setSeconds,setTime,setMilliseconds,setHours,
parse:可以用于讲一个字符串解析为Date object
alert( Date.parse("June 18, 1970") ); // 14529600000 alert( Date.parse("2010/11/11") ); // 1289451600000 var d = new Date(Date.parse("1995/04/25")); // Tue Apr 25 ▶ 1995 00:00:00 GMT-0400 (EST) alert(d);
Array methods
join:这是和split相反的动作,它将把数组中的所有元素结合在一起形成一个string
alert( ["cats", "dogs", "hamsters", "fish"].join(' ') ); // "cats dogs hamsters fish" alert( "this should not have spaces".split(" ").join("_") ); // "this_should_not_have_spaces"
pop/shift:
var arr = ["cats", "dogs", "hamsters", "fish"]; console.log( arr.pop() ); // "fish" console.log( arr.shift() ); // "cats" console.log( arr ); // ["dogs", "hamsters"]
push/unshift:push将在数组的尾部增加一个item,unshift则在数组的开始增加一个item
var arr = ["Beta", "Gamma"]; arr.push("Delta"); console.log(arr); // ["Beta", "Gamma", "Delta"]; arr.unshift("Alpha"); console.log(arr); // ["Alpha", "Beta", "Gamma", "Delta"]
reverse:
alert( ["Crockford", "Resig", "Zakas"].reverse() ); ▶ // "Zakas, Resig, Crockford"
slice:有时,你希望将你的数组slice成多个部分,这个slice就提供这个功能。包含两个参数:index of the starting element and index of the element after the last one you want to slice.
alert( ["a", "b", "c", "d", "e"].slice(2, 4) ); ▶ // ["c", "d"] alert( ["f", "g", "h", "i", "j"].slice(1) ); ▶ // ["g", "h", "i", "j"]
sort:按照字母排序:
["d", "e", "c", "a", "b"].sort(); // ["a", "b", "c", "d", ▶ "e"] [0, 5, 10, 15, 20, 25].sort();//[0, 10, 15, 20, 25, 5].
在上面对数字的排序可能并不是你希望得到的效果,一个可行的方案是sort函数传入一个函数:
var arr = [5, 2, 3, 4, 1,10,20]; arr.sort(function (a, b) { return a - b; }); console.log(arr); // [1, 2, 3, 4, 5 ,10 ,20];
Math functions
javascript也提供了一个Math 对象,你虽然不能像Date对象一样二次创建对象,但是你却可以直接调用Math对象的方法
min,max,random,round,ceil,floor,pow,
alert( Math.min(9, 1, 4, 2) ); // 1 alert( Math.random() ); // 0.5938208589795977 (you'll ▶ probably get something else) alert( Math.random() * 10 ); // 6.4271276677027345 (again,▶ your mileage may vary) alert( Math.round(10.4) ); // 10 alert( Math.round(10.5) ); // 11 alert( Math.ceil(10.4) ); // 11 alert( Math.floor(10.5) ); // 10 function getRandomNumberInRange(min, max) { return Math.floor( Math.random() * (max - min + 1) + ▶ min); } alert( getRandomNumberInRange(0, 100) ); // 39; you'll ▶ probably get something different.
This
This是javascript预留的keyword,你可以将this想象成一个你不能控制的动态变量。this变量的值将随着你在代码的哪一个地方而不断变化其值:
默认情况下,this将指向global对象。this没有一个合适的名字,但是他却有一个属性指向它自己:window.如果你看看下面这个小的代码,就有些感觉了:
var my_variable = "value"; function my_function () { return "another_value" } alert( this.my_variable ); // "value" alert( this.my_function() ); // "another_value" var global = { window : global . . . }; this = global;
这意味着你可以访问你的全局变量或者函数作为window的属性,而这比用this来访问这些全局变量函数更加普遍。这依然有点搞,因为你可以访问window对象来使用global对象上的函数或属性,即使this可以指向其他的对象。甚至,你不用使用window.xx的方式调用,除非你有一个local的变量或者函数而覆盖了全局的函数或变量!
new keyword
第一种改变this指针的方法是使用new 关键字:
你可能知道了javascript对象是什么(属性和函数的封装)。将相关的功能封装到一个合适的object中并且通过合适的接口来访问是一个非常非常非常基础的OOP编程模式。Classes就像一个蓝图:他们不是实际的对象,但是他们描述了一旦通过该class创建一个object,那么这个object应该具备的函数和变量。javascript是面向对象的,但是它却不用class这个概念。想法,它使用prototypes这个概念。prototypes是一些实际的对象objects,我们可以使用这些对象objects作为鲜活的创建其他对象的蓝图”对象“。
在其他语言中,比如c++,使用一个构造函数来创建新的对象。而在javascript中,构造函数是普通的函数,如果在它前面添加一个new关键字则可以创建新的对象。
比如我们要创建truck对象,
function Truck(model) { this.num_of_tires = 4; this.kilometers = 0; this.model = model; } var my_truck = new Truck("Hercules"); console.log(my_truck);
上面的代码注意两点:首先,我们定义所有的变量作为this对象的属性;其次,我们没有从这个函数中返回任何东西。这两点是因为我们计划使用new关键字来调用这个函数创建对象。注意我们是如何使用new关键字的。它做了两件事:首先,它更改this指针指向一个新的干净的对象。很明显,我们然后可以增加我们定制的属性在这个函数中。第二件new干的事情是:它将this返回。现在my_truck变量将指向新的对象,这个my_truck就像我们下面的代码一样的结果:
var my_truck = { num_of_tires : 4, kilometers : 0, model : "Hercules" }; console.log(my_truck);
这就是使用new的想法:我们获得一个创建新对象的超级简单的方法。很明显,你如果仅仅需要一个或两个简单的对象,你不需要这么做。通常你在需要有很多功能并且希望打包到一个对象中时,你才需要这种new的模式,或者你希望创建一系列类似的对象时,也可以使用new方法来创建对象。
call and apply
第二种更改this指针的方法是call或者apply.在javascript中一切皆为对象---即便function也是对象。既然function是对象,那么他们也可以有属性和方法(property,method)。call和apply就是每一个function对象的两个默认方法。
这两个方法存在的价值就是在函数中修改this指针:
function Truck (model, num_of_tires) { this.num_of_tires = num_of_tires; this.kilometers = 0; this.model = model; } var basic_vehicle = { year : 2011 }; Truck.call(basic_vehicle, "Speedio", 4); console.log(basic_vehicle);
小知识点:primitive vs reference values:你可能想,虽然我们更改一个已知的对象,我们会丢失那些变更,除非我们将他们赋值给一个新的变量。如果变量是一个primitive value,那么这将是对的。然而,objects(arrays)是referrnce values:values passed by referrnce.一个primitive value(比如一个string或者number)是在计算机内存中保存的,而我们使用一个变量来访问它。当我们将这个变量传给一个函数时,我们会copy那个值到一个新的内存,而将函数参数指向这个新的copy内存,而在函数中对这个copy操作,因此原始的primitive value并不会被变更。(因为内存不同),但是当我们使用reference value时则不同:当我们传递一个object或者一个array给一个function时,形式参数将指向原始object/array相同的内存。这意味着,Truck.call(basic_vehicle)中的this和函数外的basic_object中的this是完全一个basic_object.所以没有必要做任何assign动作,因为basic_vehicle现在已经有了由Truck赋值的所有属性。如果我们将Truck.call(basic_vehicle)的返回值付给一个变量,将会发生什么,它将会是undefined,因为Truck without new将不会返回任何东西!
在这里,我们通过Truck函数的call方法来调用Truck函数。call的第一个参数是我们希望在函数中this所指向的对象。既然我们没有使用new来调用Truck,它将不会创建任何新的对象或者返回this.这也是我们所希望的,因为我们仅仅是在修改已经存在的对象,而不是重新创建一个!
在将成为this所指向的对象参数后(你可以称为函数的上下文context),我们可以传入任何需要的参数。这些将作为参数被传入将被执行的函数。上面的例子中,第二个参数"Speedio"将被传给Truck函数作为其第一个Parameter。第三个参数6将成为Truck的第二个参数。
和call向对应,还有一种方法叫做apply.两者的区别是:apply只接受两个参数。第一个是context object,第二个是将被传入function的一个参数的数组。如果你有一个作为数组的参数,这种方法将非常有用。例如,
function Truck (model, num_of_tires) { this.num_of_tires = num_of_tires; this.kilometers = 0; this.model = model; } var basic_vehicle = { year : 2011 }, user_input = "Speedio 18"; Truck.apply(basic_vehicle, user_input.split(" ")); console.log(basic_vehicle);
Inside an Object
还有另外一种方法可以更改this的值。
var waitress = { name : "Ashley", greet: function (customer) { customer = customer || " there!"; return "Hi " + customer + " My name is " + ▶ this.name + "; what can I get you?"; } }; alert( waitress.greet("Joe") ); // Hi Joe My name is ▶ Ashley; what can I get you?
大家都知道使用new关键字来调用一个构造函数是一种古老而有用的创建对象的方法,比如下面的构造函数:
function Computer (name, ram_amount, memory_amount) { this.name = name; this.RAM = ram_amount; // in GBs this.space = memory_amount; // in MBs }
上面是一个普通的构造函数,如果我们使用new Computer("MacBook", 2, 250000)调用的话,我们将创建一个Computer对象。注意:Computer并不是所有computer对象的prototype(或者blueprint),Computer仅仅是这些computer对象的构造函数constructor,也就是构造产出对象own Property和own Method!!!!.
如果我们希望给上述computer对象增加一个函数,比如保存文件的能力函数将会发生什么?你可能这么做:
function Computer (name, ram_amount, memory_amount) { this.name = name; this.RAM = ram_amount; this.space = memory_amount; this.files = []; this.store_file = function (filename, filesize) { this.files.push(filename); this.space -= filesize; }; }
上面的方法看似无伤大雅,但是这里存在一个问题。你不应该在你的constructor function中创建function,因为每次你创建一个新的对象copy时,你将会创建那个function的新的copy,而这将占用内存。关键是:没有必要这样做,因为我们可以仅仅保存那个function的一个copy,而该copy将为所有的Computer object服务。这之所以工作,是因为我们在构造函数中使用this来引用了对象本身。
我们需要做的是一种方法用于将每一份Computer对象指向一个store_file函数的方法。我们迄今所讨论的方法是有效的,因为function是一个method,所以this指向一个对象。我们可以这样做--并且只需要一个函数--而使用Computer构造函数的prototype属性:这意味着使用new Computer创建对象时,除了Computer构造函数own property/own method以外,还有一个隐形的__proto__原型链生成以便供继承使用!
Computer.prototype.store_file = function (filename, filesize) { this.files.push(filename); //this指的是new Computer的返回对象 this.space -= filesize; };
记住:function本身也是object哦,所以他们可以有属性的,在上面这个代码片段中,我们访问了Computer构造函数的prototype属性,而该属性的值实际上是一个object,该对象是所有Computer objects用于inherit的祖。这意味着:当你运行: my_computer.store_file("image.jpg",26)时,js引擎发现my_computer本身并没有一个叫做store_file的method,所以js引擎会继续查看my_computer的prototype object(Computer.prototype来指示)(通过my_computer.__proto__来寻址),如果在prototype中发现这个方法则调用它,如果没有发现,则继续向prototype chain中继续向上搜索,直到最上层Object.prototype---Object是javascript中的所有对象的父亲。
在这个使用new+constructor来创建新的对象的案例中,有两点需要说明:
1. 构造函数本身创建一个对象(denoted by this inside the function);在这个函数中,我们对该对象的属性进行赋值操作;
2. 每一个使用指定constructor创建的任何一个对象的prototype或者说parent object都保存在ConstructorFunction.prototype(注意构造函数本身也是一个对象)里。所有prototype/parent对象的属性或者方法都可以从"child"object中来访问(机制是由js的prototype chain检索来实现的)
这就是为什么被称为"Prototypal Inheritance";每一个从相同的构造函数创建的对象都继承于相同的prototype。很重要的一点是:如果一个给定的child object有一个它自己的属性,比如说my_obj.name,那么prototype不会被检索该属性。同时,如果你修改一个child object的属性或者方法,你只修改了那个对象本身,并不会影响到prototype object。
以下为例:
function Product(name) { if (name) { this.name = name; } } Product.prototype.name = "To be announced"; //提供默认的name,如果new Product时传入name则覆盖这个原型上的默认值 Product.prototype.rating = 3; var ipad = new Product("iPad"); alert( ipad.name ); // "iPad"; alert( ipad.rating ); // 3
// ipad from above
ipad.rating = 4;
ipad.rating; // 4
Product.prototype.rating; // 3
这里我们创建了一个简单的Product对象,你可以看到name属性打印的是ipad对象本身修改过后的name,而rating属性则是来自prototype的继承(实际上,如果你在child object上修改也会覆盖掉prototype的属性)。通过修改rating这个值,我们实际上就是在ipad对象上添加了一个rating属性,因为对象的原型对象上的属性是只读的!!,既然ipad对象自己有了rating属性,当我们访问ipad.rating属性时,js引擎将不再向上级prototype检索。然而,即便如此,prototype的rating属性值也没有被改变。
原型链
this的scope
当一个函数没有使用" . "来调用该函数时,这个函数中的this指针是和这个函数被调用前面一行的this是指向同一个对象的
比如:
var joe={ firstName: 'joe', lastName: 'will', fullName: function() { return this.firstName + ' ' + this.lastName; } } var fullName = joe.fullName; var firstName = 'John'; var lastName = 'papa'; console.log(fullName()); //注意由于fullName()函数被直接调用,未用.方式来引用,因此fullName函数中的this和他前面一行指向是相同的。在这里也就是global的空间,而全局空间中确实有firstName和lastName,故而输出” John papa“
另外一个简单的例子,通过jquery定位到所有的"all" button,点击以后所有的answer div内容将变为button的text:
html: Mark ALL test:???javascript: $(function(){ $('.all').click(function(){ //在这个context时,this是指$('.all')即button var that = this; $('.answer').each(function(){
//这里this是指各个有.answer类的单个div $(this).text($(that).text()); }); }); });
Object Methods
迄今为止,我们讨论除了Objects外的所有type的方法,之所以这么安排是因为:
1.我希望保持所有object-related资料在一个集中的地方;
2.大多数你可以使用的methods就是你自己创建的那些方法
然而,对于object对象本身也有一些built-in的方法。既然你创建的任何对象都是一个Object(即:从Object.prototype来继承的),那么这些方法都是随时可用的:
hasOwnProperty
function Person(name) { this.name = name; } Person.prototype.legs = 2; var person = new Person("Joe"), prop; for (prop in person) { console.log(prop + ": " + person[prop]); } // in console: // name : Joe // legs: 2
在这里,如果你的对象是从有着属性的prototype继承过来的,那么那些属性本身也会被loop检索到。这可能会打印出你不希望看到的结果(比如从prototype中继承出来的leg)。在这种情况下,对象可以使用一个实用的方法hasOwnProperty来决定一个给定的property是否属于object本身还是它的prototype.
function Person(name) { this.name = name; } Person.prototype.legs = 2; var person = new Person("Joe"), prop; for (prop in person) { if (person.hasOwnProperty(prop)) { console.log(prop + ": " + person[prop]); } } // console: // name: Joe
toString
每一个object都有一个toString方法。这个方法在object在任何地方将被打印时调用;它的作用是转换对象为一个字符串。不同的built-in types将做不同的事情:strings明显不会改变;数字将会成为数字的字符串;dates将格式化为date strings。但是一般的object将会怎样呢?
var o = { name : "Andrew" }; alert( o.toString()); // "[object Object]"
上述代码实际上毫无用处,只是告诉你它是一个object。如何解决这个问题呢?你可以定义你自己的toString函数来覆盖prototype中的这个函数
var person = { name : "Joe", age : 30, occupation: "Web Developer", toString : function () { return this.name + " | " + this.occupation; } }; alert( person.toString() ); // "Joe | Web Developer"
valueOf:
该函数将返回一个primitive value以便代表你的对象。比如:
var account = { holder : "Andrew", balance : 100, valueOf : function () { return this.balance; } }; alert( account + 10 ); // 110
Object.create:creating object from object
var Human = { arms: 2, legs: 2, walk: function() { console.log("Walking"); } } << {"arms": 2, "legs": 2, "walk": function () ➥{ console.log("Walking"); }} lois = Object.create(Human); << {"arms": 2, "legs": 2, "walk": function () ➥{ console.log("Walking"); }}
Closure
closure是javascript最重要的一个功能之一。如果你熟悉其他的编程语言(特别是oop的语言,比如c++),你一定知道private variable(私有变量)这个概念。一个私有变量就是一个仅仅被对象的方法能够访问而不能在对象之外直接访问的变量。Javascript本身并没有这个功能,但是我们可以使用closure来"make"private variables。记住两点关于贡献于closure的javascript特性:
1.everyting is an object, even functions.这意味着我们可以从一个函数返回一个函数
2.Anonymous,self-invoking functions是没有名字的立即运行而通常只运行一次的函数
现在我们使用这两个概念来创建一个closure。首先看看通常的javascript代码:
var secretNumber = 1024; function revealSecret(password) { if (password === "please") { return secretNumber++; } return 0; }
上面这段代码对你来说是很普通的。我们有一个叫做secretNumber的变量和一个如果密码正确返回加1的函数。在这里,secretNumber变量本身没有任何秘密可言,他是一个任何函数都能访问的全局变量。为了增加私密性,为什么我们不将它放到函数内部呢?这样我们从函数外面就将不能访问这个变量了!
不过注意了,我们不能那样做哦,因为如果那样,每次我们调用那个函数,js引擎都将reset setcretNumber变量到1024!怎么做呢?Closure可以完成上面的场景!
var revealSecret = (function () { var secretNumber = 1024; return function (password) { if (password === "please") { return secretNumber++; } return 0; }; }()); alert( revealSecret("please") ); // 1024 alert( revealSecret("please") ); // 1025
注意revealSecret本身被赋予一个anonumous self-invoking function的返回值。既然那个匿名函数立即运行,revealSecret就将被赋予该函数的返回值。在这个例子中,它又是一个函数。下面的事实令人印象深刻:内部函数(被返回的函数)本身引用了secretNunber变量从它的"parent"function's scope.即使匿名函数已经返回,inner function依然能够访问到这个变量。那就是closure:an inner function having access to the scope of it's parent function, even after the parent function has returned!!!这种方法,secretNUmber仅仅被初始化一次,它可以在每次内部函数调用时加1,而任何其他人不能访问它,因为它被匿名函数的scope所保护。总的来说:closure是暴露从函数外来访问一个函数的scope的有限控制(closure is the concept of exposing limited control of a function's scope outside the function.)
这种closure在创建模块时也非常有用:
var a_module = (function () { var private_variable = "some value"; function private_function () { // some code; } return { public_property : "something" // etc : " ... " }; }());
这就是module pattern:在一个anonymous selft-invoking function中,我们创建无法在匿名函数外访问的变量和方法。然后,我们返回一个对象,这个对象包含public属性和能够访问匿名函数闭包里面的私有变量或者方法的公共方法。通过这种方式,那些不必暴露的信息被屏蔽,而我们只需要暴露共有的函数或者变量。
Errors
有两种错误:一种是当你写代码时犯的错误,比如语法错误或者类型错误,另一种是无法预知的错误,比如用户的输入错误或者missing feature in the js engine.虽然js引擎通常默默地处理晓得错误,比如忘记了分号,一些大的严重错误js会抛出异常。当然,我们也可以主动抛出我们自己的错误异常。
try { // code that might cause an error here } catch (e) { // deal with the error here }
我们将可能产生错误的代码封装在try块中。如果我们不这样做,js引擎会处理那个错误,这可能意味着停止执行你的代码并且可能向用户显示错误:而这可能并不是我们想要的。如果一个错误被抛出,error object将被传递到catch block。在catch代码块中,你可以处理这个错误。
在error对象中,不同的web浏览器包含不同的属性。他们都会包含error name和error message.Firefox也会包含文件名和文件行。Chrome包含一个stack trace.也有一些其他的东西,但是主要的就是name和message.
function make_coffee(requested_size) { var sizes = ["large", "medium", "small"], coffee; if (sizes.indexOf(requested_size) < 0) { throw new Error("We don't offer that size"); } coffee = { size: required_size, added: ["sugar", ▶ "sugar", "cream" ] }; return coffee; } try { make_coffee("extra large"); } catch (e) { console.log(e.message); }
Events:
什么是event?
event,简单地说,就是在DOM中的元素上发生的事件,比如:
1.the page loaded;2.an element was clicked;3.the mouse moved over an element;4.when something like this happnes, you may often want to do something.比如,当page loaded,运行一段js代码,或者当元素被clicked时,执行这个函数。。。
常见的event事件有:click,mouseover,mouseout,keypress,load,submit
正如你所看到的事件可以分为两类:一类为用户的行为所导致的事件,一类是浏览器的行为所导致的事件。上面的简单列表中:用户执行click,mouseover,out和keypress事件;浏览器fires the load event when the page has finished loading, and the submit event when the user clicks a submit button(so it is almost a user-performed events)
Adding Event handlers:
An event is fired on a given DOM element.这意味着我们可以wireup一个event handler(或者event listener)to a given element.一个event handler是一个当一个给定事件发生时需要执行的函数。比如下面的代码:
// var btn = document.getElementById("some_button"); function welcome_user(evt) { alert("Hello there!"); } btn.addEventListener("click", welcome_user, false);
在event中有一个概念bubbling and capturing:
<div> <span> Click Here! span> div>
看上面的代码,如果我们点击span,因为span在div元素内,所以当我们点击span时,div元素也将被点击。所以两个元素div和span豆浆有一个click事件被触发。如果两个元素都有eventhandler侦听,那么豆浆被调用。问题是:谁先被执行?这就是capturing和bubbling的背景了。DOM标准说事件应该首先capture随后bubble. Capture阶段是当事件从最高级别的元素开始向下扩散。在这里,click handler of div将被先执行。一旦event到了最底部,或者最里面,event将被bubble。Bubble阶段将从最底部不断向上冒泡。我们再回到addEventListener的第三个参数是用于指定是否在capture phase来处理。由于IE9之前不支持capturing,那么通常false是安全的(bubbling)。