作者Rebecca Murphey
原文链接地址http://jqfundamentals.com/
With contributions by James Padolsey, Paul Irish, and others. See the GitHub repository for a complete history of contributions.
Copyright © 2011
【译】jquery基础教程(jQuery Fundamentals)——(第一部分)概述
【译】jquery基础教程(jQuery Fundamentals)——(第二部分)javascript基础
【译】jquery基础教程(jQuery Fundamentals)——(第三部分)jquery基础
【译】jquery基础教程(jQuery Fundamentals)——(第四部分)jquery核心
【译】jquery基础教程(jQuery Fundamentals)——(第五部分)jquery事件
【译】jquery基础教程(jQuery Fundamentals)——(第六部分)jquery效果
【译】jquery基础教程(jQuery Fundamentals)——(第七部分)jquery Ajax
【译】jquery基础教程(jQuery Fundamentals)——(第八部分)jquery插件
【译】jquery基础教程(jQuery Fundamentals)——(第九部分)jquery最佳性能实践
【译】jquery基础教程(jQuery Fundamentals)——(第十部分)jquery代码组织
【译】jquery基础教程(jQuery Fundamentals)——(第十一部分)jquery定制事件
Jquery 是建立在javascript语言之上的、丰富的、非常具有表现力的语言。这一章节涵盖了javascript的基础概念,同时也包含了一些对于以前没有用过javascript的人来说的经常性的陷阱。这对于哪些没有编程经验的人来说会有特殊的价值,即使对哪些运用其他编程语言的人来说,学习一些javascript的特殊性也是有所裨益的。
如果你对进一步学习javascript有兴趣的话,我强烈的推荐你这本书:JavaScript: The Good Parts by Douglas Crockford.
理解 语句,变量命名,空格,和其他的javascript语法。
例2.1 变量声明例子
var foo = 'hello world';
例2.2:在引号外面的空格是没有意义的
var foo = 'hello world';
例2.3:圆括号改变优先级
2 * 3 + 5; // returns 11; multiplication happens first 2 * (3 + 5); // returns 16; addition happens first
例2.4:跳格设定仅仅是增强代码可读性,并没有特殊意义
var foo = function() { console.log('hello'); };
l 基本操作符
基本的运算符允许你操作各种值。
例2.5: 连结
var foo = 'hello'; var bar = 'world'; console.log(foo + ' ' + bar); // 'hello world'
例2.6:乘和除
2 * 3; 2 / 3;
例2.7:自增和自减
var i = 1; var j = ++i; // pre-increment: j equals 2; i equals 2 var k = i++; // post-increment: k equals 2; i equals 3
l 对数字和字符串的操作符
在javascript中的数字和字符串的表现方式或许会超出你的预料之外
例2.8: 加法和连结
var foo = 1; var bar = '2'; console.log(foo + bar); // 12. uh oh
例2.9:强制转换字符串成数字类型
var foo = 1; var bar = '2'; // coerce the string to a number console.log(foo + Number(bar));
Number的构造器,当被用为方法(如上例)调用的时候,会产生将其参数类型转换成number类型的效果。你也可以用一元+操作,它也可以产生相同的效果:
例2.10:string类型强制转换成number类型(用一元+操作符)
console.log(foo + +bar);
l 逻辑操作符
逻辑操作符允许你计算一系列的用AND和OR操作的操作对象的值。
例2.11:逻辑与(&&)和或运算(||)
var foo = 1; var bar = 0; var baz = 2; foo || bar; // returns 1, which is true bar || foo; // returns 1, which is true foo && bar; // returns 0, which is false foo && baz; // returns 2, which is true baz && foo; // returns 1, which is true
这个例子可能不太清晰,||操作返回操作对象中的真值,如果没有真值,则返回假值。&&操作返回操作对象中的假值,或者操作对象中都是真值的时候返回真值。(这段没有太理解,翻译的有可能不准确,有哪位解惑的可以提下)
你务必要查阅下 the section called “Truthy and Falsy Things”,来获得对于如何求得true和如何求得false的详细方法。
注解
你也许经常看到开发人员运用这些逻辑操作符来替代if语句完成控制例如:
// do something with foo if foo is truthy foo && doSomething(foo); // set bar to baz if baz is truthy; // otherwise, set it to the return // value of createBar() var bar = baz || createBar();
这种格式非常的优雅和简洁;另一方面,它也相当的难以理解,尤其是对于初学者。我现在把他在这里提出来,在你阅读代码的时候你就会认得它,但是除非你对它所包含的意思已经理解并且运用自如,否则我并不建议你用它.
l 比较运算符
比较运算符允许你测试操作对象的值是否相等,或者它们是否是同一个对象。
例2.12:比较运算符
var foo = 1; var bar = 0; var baz = '1'; var bim = 2; foo == bar; // returns false foo != bar; // returns true foo == baz; // returns true; careful! foo === baz; // returns false foo !== baz; // returns true foo === parseInt(baz); // returns true foo > bim; // returns false bim > baz; // returns true foo <= baz; // returns true
有时,你想在特定的条件下才运行一块代码。流程控制——通过if和else语句块——提供了这种功能。
例2.13: 控制语句
var foo = true; var bar = false; if (bar) { // this code will never run console.log('hello!'); } if (bar) { // this code won't run } else { if (foo) { // this code will run } else { // this code would run if foo and bar were both false } }
注解
虽然大括号并不是必须的,尤其是在单行的条件语句的时候,不过为了是语句更有可读性,最好还是加上。
切忌不要在if/else语句块中多次定义举要相同名称的的函数,那样的话程序的执行结果会超出你的预期。
l 真值和假值
为了流程控制的成功实现,理解那些值是真值和那些值是假值变的尤为重要。有的时候,值看起来应该是一种,但实际上确实另一个结果。
例2.14: 计算出true的值
'0'; 'any string'; []; // an empty array {}; // an empty object 1; // any non-zero number
例2.15:计算出false的值
0; ''; // an empty string NaN; // JavaScript's "not-a-number" variable null; undefined; // be careful -- undefined can be redefined!
l 三元运算符实现的条件语句
有的时候,你想依据一些条件来给一个变量进行赋值。你可以用if/else语句,但是在很多情况下,三元运算符用起来更方便一些。【定义:三元运算符检查一个条件,如果这个条件是true,则返回一个特定的值,否则返回一个不同的值】
例2.16:三元运算符
// set foo to 1 if bar is true; // otherwise, set foo to 0 var foo = bar ? 1 : 0;
然而三元运算符可以在不使用返回值的情况下分配给一个变量,这是比较蛋疼的。
l Switch语句
相比大量的运用if/else语句块,有的时候用Switch语句来代替他们,则是很明智的做法【定义;Switch语句是根据一个变量或者表达式的值来确定不同的语句块】
例2.17: switch语句
switch (foo) { case 'bar': alert('the value was bar -- yay!'); break; case 'baz': alert('boo baz :('); break; default: alert('everything else is just ok'); break; }
Switch语句在javascript 中已经有些不在受到青睐了,因为人们经常通过创建对象的方式来完成相同的实现,这样对于代码的复用和调试等更有扩展性。例如:
var stuffToDo = { 'bar' : function() { alert('the value was bar -- yay!'); }, 'baz' : function() { alert('boo baz :('); }, 'default' : function() { alert('everything else is just ok'); } }; if (stuffToDo[foo]) { stuffToDo[foo](); } else { stuffToDo['default'](); }
在本章节的后面我们会更深入讨论这个问题。
循环允许你运行一块代码块特定的次数。
例2.18:循环
// logs 'try 0', 'try 1', ..., 'try 4' for (var i=0; i<5; i++) { console.log('try ' + i); }
注意,在循环中,虽然我们在变量i的前面江上了var关键字,但这并不会限定变量i运用到循环语句块中。我们在本章节后续的内容中会进一步讲解。
l For 形式的循环
一个for循环语句是由4部分按照如下的结构组合在一起的:
for ([initialisation]; [conditional]; [iteration]) [loopBody]
Initialisation语句只在循环开始之前执行一次。它为你提供了一个准备和声明变量的机会。
Conditional语句在每次迭代之前都会执行一次,它的返回值决定循环是否继续执行下去。如果条件语句计算出false值,循环停止。
Iteration语句在每次迭代之后执行,它为你提供了一个改变重要变量的状态的机会。比较有代表性的方式是,它会包含一个自增或者自减的计数器以上循环不停的向结束的条件靠近。
Loopbody 语句则是每次迭代中被执行的部分。它可以包含所有你想放进去的东西。一般情况下,它会包含多条语句被执行,所以这些语句最好是被大括号包裹起来({…..})。
下面是一个典型的for循环例子:
例2.19: 典型for循环
for (var i = 0, limit = 100; i < limit; i++) { // This block will be executed 100 times console.log('Currently at ' + i); // Note: the last log will be "Currently at 99" }
l While循环
Wihle循环看起来比较像if语句,它的效果是需要执行的主体部分会一直执行到条件值计算出false为止。
while
([conditional]) [loopBody]
下面是典型的while循环:
例2.20: 典型的wihle循环
var i = 0; while (i < 100) { // This block will be executed 100 times console.log('Currently at ' + i); i++; // increment i }
你或许注意到了一个现象,我们在循环体中自增了计数器。当然你也可以将条件语句和自增语句组合在一起,像这样:
例2.21: 条件语句和自增语句组合在一起的while循环
var i = -1; while (++i < 100) { // This block will be executed 100 times console.log('Currently at ' + i); }
注意我们将变量i的初始值设置为了-1,并且用了前置的自增(++i)。
l Do-While循环
这个循环和while循环基本一样,但是会有这样一个效果出现:循环体在循环条件被检测之前至少会被执行一次。
do [loopBody] while ([conditional])
下面是do-while循环:
例2.22: do-while循环:
do { // Even though the condition evaluates to false // this loop's body will still execute once. alert('Hi there!'); } while (false);
当然像这样至少需要被执行一次的循环的情况非常的少见,不过不管怎样,知道这点总是好的。
通常情况下,循环终止是通过循环的条件语句没有计算出true值来实现的,但是在循环的中间,通过循环体中的break语句也是可以终止循环的。
例2.23: 终止循环
for (var i = 0; i < 10; i++) { if (something) { break; } }
或者你想继续循环,但是却不想执行本次循环剩下的循环体中的内容。Continue语句正式用在这样的场合。
例2.24:跳到循环的下次迭代
for (var i = 0; i < 10; i++) { if (something) { continue; } // The following statement will only be executed // if the conditional 'something' has not been met console.log('I have been reached'); }
Javascript 拥有很多的保留字,或者可以说成在语言中拥有特殊含义的字。除了应用它们在语句中包含的特殊含义的情况下,你最好避免使用这些字。
数组是0为起始下标的值得列表。对于存储一些相关的并且具有相同数据类型的数据条目(例如string),它不失为一种很便利的方法。数组可以存储多种类型的数据,包括其他的数组。
例2.25: 简单的数组
var myArray = [ 'hello', 'world' ];
例2.26: 通过下标访问数组
var myArray = [ 'hello', 'world', 'foo', 'bar' ]; console.log(myArray[3]); // logs 'bar'
例2.27: 检测数组大小
var myArray = [ 'hello', 'world' ]; console.log(myArray.length); // logs 2
例2.28:改变数组中项的值
var myArray = [ 'hello', 'world' ]; myArray[1] = 'changed';
通过上例是可以改变数组中的一个项,但这种方式并不被推崇。
例2.29:向数组中添加元素
var myArray = [ 'hello', 'world' ]; myArray.push('new');
例2.30: 利用数组工作
var myArray = [ 'h', 'e', 'l', 'l', 'o' ]; var myString = myArray.join(''); // 'hello' var mySplit = myString.split(''); // [ 'h', 'e', 'l', 'l', 'o' ]
对象包含了一个或者多个键值对。键的部分可以是任何的字符串。值得部分则可以是任何类型:数字,字符串,数组,函数,甚至是另一个对象。
【定义:当这些类型中用到是函数的时候,对象则被称为 对象的方法,反之则被称为属性】
不难看出javascript中几乎任何东西都是对象——数组,函数,数字,甚至是字符串——他们都包含属性和方法。
例2.31: 创建一个对象文字
var myObject = { sayHello : function() { console.log('hello'); }, myName : 'Rebecca' }; myObject.sayHello(); // logs 'hello' console.log(myObject.myName); // logs 'Rebecca'
注解
当你创建一个对象的时候,你应该注意到每个键值对的键部分可以是任意一个javascript中有效的标识符,字符串(被包含在“”中)或者是数字
var myObject = {
validIdentifier: 123,
'some string': 456,
99999: 789
};
对象对组织代码非常的有效;要想获得更多的信息,你可以访问 Using Objects to Organize Your Codeby Rebecca Murphey:
函数包含了需要反复被执行的代码块。函数可以有零个或者多个参数,并且可以返回也可以不返回值。
函数可以通过多种方式被创建:
例2.32: 函数声明
function foo() { /* do something */ }
例2.33:命名函数的表达式
var foo = function() { /* do something */ }
由于一些比较深入的技术性原因( in-depth and technical reasons)我比较喜欢通过命名函数表达式的方式来确定一个函数的名字。当然了,你会在其他人的javascript代码中看到这两种方式。
l 运用函数
例2.34: 简单函数
var greet = function(person, greeting) { var text = greeting + ', ' + person; console.log(text); }; greet('Rebecca', 'Hello');
例2.35:带有返回值的函数
var greet = function(person, greeting) { var text = greeting + ', ' + person; return text; }; console.log(greet('Rebecca','hello'));
例2.36:返回值是另一个函数的函数
var greet = function(person, greeting) { var text = greeting + ', ' + person; return function() { console.log(text); }; }; var greeting = greet('Rebecca', 'Hello'); greeting();
l 自执行的匿名函数
自执行的匿名函数是javascript中一个比较常见的模式。这种模式先是创建一个函数表达式然后立即执行这个函数。这种模式一般用在你要避免你的代码污染了全局的命名空间的情况下——在函数内部声明的变量对于外部是不可见的。
例2.37:自执行的匿名函数
(function(){ var foo = 'Hello world'; })(); console.log(foo); // undefined!
l 参数为函数的函数
在javascript中,函数是“一等公民”,他们可以被指定为变量,也可以作为参数传递到其他函数中。将函数作为参数进行传递在jquery中是极其常见的现象。
例2.38:传递一个匿名函数作为参数
var myFn = function(fn) { var result = fn(); console.log(result); }; myFn(function() { return 'hello world'; }); // logs 'hello world'
例2.39:传递一个命名的函数作为参数
var myFn = function(fn) { var result = fn(); console.log(result); }; var myOtherFn = function() { return 'hello world'; }; myFn(myOtherFn); // logs 'hello world'
Javascript提供了一个测试变量类型的方法。然而,结果往往让人费解——例如,一个数组的类型竟然是“对象”。
当你想获取一个具体值得类型的时候,typeof操作符是一个比较常用的方式。
例2.40:检查不同变量的类型
var myFunction = function() { console.log('hello'); }; var myObject = { foo : 'bar' }; var myArray = [ 'a', 'b', 'c' ]; var myString = 'hello'; var myNumber = 3; typeof myFunction; // returns 'function' typeof myObject; // returns 'object' typeof myArray; // returns 'object' -- careful! typeof myString; // returns 'string'; typeof myNumber; // returns 'number' typeof null; // returns 'object' -- careful! if (myArray.push && myArray.slice && myArray.join) { // probably an array // (this is called "duck typing") } if (Object.prototype.toString.call(myArray) === '[object Array]') { // Definitely an array! // This is widely considered as the most robust way // to determine if a specific value is an Array. }
Jquery提供了一些工具方法来帮助你判断一个任意值得类型。这个后面会讲道。
就像大多数面向对象语言一样,在javascript中,this 是一个特殊的关键字,它通常被用来指向一个方法正在被调用的对象。确定this的值需要下面简单的几部:
例2.41:通过function.call调用函数
var myObject = { sayHello : function() { console.log('Hi! My name is ' + this.myName); }, myName : 'Rebecca' }; var secondObject = { myName : 'Colin' }; myObject.sayHello(); // logs 'Hi! My name is Rebecca' myObject.sayHello.call(secondObject); // logs 'Hi! My name is Colin'
例2.42:用function.bind来调用函数
var myName = 'the global object', sayHello = function () { console.log('Hi! My name is ' + this.myName); }, myObject = { myName : 'Rebecca' }; var myObjectHello = sayHello.bind(myObject); sayHello(); // logs 'Hi! My name is the global object' myObjectHello(); // logs 'Hi! My name is Rebecca'
例2.43:将函数在运行时附加到一个对象上
var myName = 'the global object', sayHello = function() { console.log('Hi! My name is ' + this.myName); }, myObject = { myName : 'Rebecca' }, secondObject = { myName : 'Colin' }; myObject.sayHello = sayHello; secondObject.sayHello = sayHello; sayHello(); // logs 'Hi! My name is the global object' myObject.sayHello(); // logs 'Hi! My name is Rebecca' secondObject.sayHello(); // logs 'Hi! My name is Colin'
注解:
当在一个很长得命名空间中调用函数时,为了减少代码,你将一个引用存储到一个单独并且短小的变量上,这太吸引人了。不过很遗憾的告诉你,这么做不对,这样会致使函数中this的值改变,导致错误的代码操作。例如:
var myNamespace = { myObject : { sayHello : function() { console.log('Hi! My name is ' + this.myName); }, myName : 'Rebecca' } }; var hello = myNamespace.myObject.sayHello; hello(); // logs 'Hi! My name is undefined'
然而,对于方法被调用的对象,你可以安全的进行任何的简化。
var myNamespace = { myObject : { sayHello : function() { console.log('Hi! My name is ' + this.myName); }, myName : 'Rebecca' } }; var obj = myNamespace.myObject; obj.sayHello(); // logs 'Hi! My name is Rebecca'
作用域是指变量能够存在一段代码中的特定的时间片。对于作用域的理解如果不到为的话,会导致你在调试的时候和抓狂。
当一个变量在函数中用var来声明后,他仅仅是对于方法内部的代码是可用的——在函数外面的代码并不能够访问到它。换句话说,声明变量的函数内部的函数可以访问变量。
此外,在函数内部声明的变量,如果没有var关键字,它就会被认为不是函数内部的变量——javascript将会转换作用域链直到window的作用域,来查找变量在何处被定义。如果变量没有被预先定义,它会被强制的定义在全局的作用域,这是我们非常不愿意看到的结果。
例2.44: 在相同的作用域中定义的函数可以访问变量
var foo = 'hello'; var sayHello = function() { console.log(foo); }; sayHello(); // logs 'hello' console.log(foo); // also logs 'hello'
例2.45:在变量作用域之外的代码无法访问到变量
var sayHello = function() { var foo = 'hello'; console.log(foo); }; sayHello(); // logs 'hello' console.log(foo); // doesn't log anything
例2.46: 具有相同命名的变量在不同的作用域中可以拥有不同的值
var foo = 'world'; var sayHello = function() { var foo = 'hello'; console.log(foo); }; sayHello(); // logs 'hello' console.log(foo); // logs 'world'
例2.47:当方法被定义后,它也会了解到变量的值是否发生了改变
var myFunction = function() { var foo = 'hello'; var myFn = function() { console.log(foo); }; foo = 'world'; return myFn; }; var f = myFunction(); f(); // logs 'world' -- uh oh
例:2.48:坑爹的作用域
// a self-executing anonymous function (function() { var baz = 1; var bim = function() { alert(baz); }; bar = function() { alert(baz); }; })(); console.log(baz); // baz is not defined outside of the function bar(); // bar is defined outside of the anonymous function // because it wasn't declared with var; furthermore, // because it was defined in the same scope as baz, // it has access to baz even though other code // outside of the function does not bim(); // bim is not defined outside of the anonymous function, // so this will result in an error
闭包是作用域概念的一个延伸——函数可以访问函数被创建的地方的作用域内的可以用的变量。如果这个说法令你和费解,不用担心:闭包最好的理解方法是通过实例。
在例2.47中,我们看到了函数怎样访问改变的变量值。同样的情况发生在定义在循环里的方法中——方法能够知晓变量的值,即使是方法被确定后,结果每个点击都弹出警告信息5.
例2.49:怎样锁定i的值
/* this won't behave as we want it to; */ /* every click will alert 5 */ for (var i=0; i<5; i++) { $('<p>click me</p>').appendTo('body').click(function() { alert(i); }); }
例2.50:通过闭包锁定i的值
/* fix: “close” the value of i inside createFunction, so it won't change */ var createFunction = function(i) { return function() { alert(i); }; }; for (var i=0; i<5; i++) { $('<p>click me</p>').appendTo('body').click(createFunction(i)); }
例2.51:利用闭包同事访问对象实例的内部和外部。
var outerObj = { myName : 'outer', outerFunction : function () { // provide a reference to outerObj through innerFunction's closure var self = this; var innerObj = { myName : 'inner', innerFunction : function () { console.log(self.myName, this.myName); // logs 'outer inner' } }; innerObj.innerFunction(); console.log(this.myName); // logs 'outer' } }; outerObj.outerFunction();
这种机制对于处理函数回调非常有用,对于上面的例子,比较好的方式是利用function.bind来调用,那样的话会避免跨域的访问。