JavaScript 的应用场合极其普遍。简单到幻灯片、照片库、浮动布局和响应按钮点击。复杂到游戏、2D 和 3D 动画、大型数据库驱动程序,等等。javascript
JavaScript 至关简洁,却很是灵活。开发者们基于 JavaScript 核心编写了大量实用工具,可使 开发工做事半功倍。其中包括:java
在应用程序中,使用变量来做为值的符号名。变量的名字又叫作标识符,其须要遵照必定的规则。数据库
一个 JavaScript 标识符必须以字母、下划线(_)或者美圆符号($)开头;后续的字符也能够是数字(0-9)。由于 JavaScript 语言是区分大小写的,因此字母能够是从“A”到“Z”的大写字母和从“a”到“z”的小写字母。编程
变量 | 解释 |
---|---|
string | 字符串(一串文本)。字符串的值必须将用引号(单双都可,必须成对)扩起来。 |
Number | 数字。无需引号。 |
Boolean | 布尔值(真 / 假)。 true/false 是 JS 里的特殊关键字,无需引号。 |
Array | 数组,用于在单一引用中存储多个值的结构 |
Object | 对象,JavaScript 里一切皆对象,一切皆可储存在变量里。这一点要牢记于心。 |
字符串转换为数字
有一些方法能够将内存中表示一个数字的字符串转换为对应的数字。parseInt()和parseFloat()json
parseInt 方法只能返回整数,因此使用它会丢失小数部分。另外,调用 parseInt 时最好老是带上进制(radix) 参数,这个参数用于指定使用哪种进制。数组
将字符串转换为数字的另外一种方法是使用一元加法运算符。浏览器
"1.1" + "1.1" = "1.11.1" (+"1.1") + (+"1.1") = 2.2 // 注意:加入括号为清楚起见,不是必需的。
var 声明全局变量和局部变量
let 声明块做用域的局部变量
const 声明一个常量app
使用var和let声明的变量时没有赋予初始值的,其值为undefined,框架
var a; console.log("The value of a is " + a); // a 的值是 undefined console.log("The value of b is " + b);// b 的值是 undefined var b; console.log("The value of c is " + c); // 未捕获的引用错误: c 未被定义 let x; console.log("The value of x is " + x); // x 的值是 undefined console.log("The value of y is " + y);// 未捕获的引用错误: y 未被定义 let y;
你可使用 undefined 来判断一个变量是否已赋值。在如下的代码中,变量input未被赋值,所以 if 条件语句的求值结果是 true异步
var input; if(input === undefined){ doThis(); } else { doThat(); }
var n = null; console.log(n * 32); // 在控制台中会显示 0
在函数以外声明的变量,叫作全局变量,由于它可被当前文档中的任何其余代码所访问。在函数内部声明的变量,叫作局部变量,由于它只能在当前函数的内部访问。
ECMAScript 6 以前的 JavaScript 没有 语句块 做用域;相反,语句块中声明的变量将成为语句块所在函数(或全局做用域)的局部变量。例如,以下的代码将在控制台输出 5,由于 x 的做用域是声明了 x 的那个函数(或全局范围),而不是 if 语句块。
if (true) { var x = 5; } console.log(x); // 5
若是使用 ECMAScript 6 中的 let 声明,上述行为将发生变化。
if (true) { let y = 5; } console.log(y); // ReferenceError: y 没有被声明
JavaScript 变量的另外一个不一样寻常的地方是,你能够先使用变量稍后再声明变量而不会引起异常。这一律念称为变量提高;JavaScript 变量感受上是被“提高”或移到了函数或语句的最前面。可是,提高后的变量将返回 undefined 值。所以在使用或引用某个变量以后进行声明和初始化操做,这个被提高的变量仍将返回 undefined 值。
/** * 例子1 */ console.log(x === undefined); // true var x = 3; /** * 例子2 */ // will return a value of undefined var myvar = "my value"; (function() { console.log(myvar); // undefined var myvar = "local value"; })(); //详细解释就是 /** * 例子1 */ var x; console.log(x === undefined); // true x = 3; /** * 例子2 */ var myvar = "my value"; (function() { var myvar; console.log(myvar); // undefined myvar = "local value"; })();
因为存在变量提高,一个函数中全部的var语句应尽量地放在接近函数顶部的地方。这个习惯将大大提高代码的清晰度。
在 ECMAScript 6 中,let(const)将不会提高变量到代码块的顶部。所以,在变量声明以前引用这个变量,将抛出引用错误(ReferenceError)。这个变量将从代码块一开始的时候就处在一个“暂时性死区”,直到这个变量被声明为止。
console.log(x); // ReferenceError let x = 3;
对于函数来讲,只有函数声明会被提高到顶部,而函数表达式不会被提高。
/* 函数声明 */ foo(); // "bar" function foo() { console.log("bar"); } /* 函数表达式 */ baz(); // 类型错误:baz 不是一个函数 var baz = function() { console.log("bar2"); };
你能够用关键字 const 建立一个只读的常量。常量标识符的命名规则和变量相同:必须以字母、下划线(_)或美圆符号($)开头并能够包含有字母、数字或下划线。
const PI = 3.14;
常量不能够经过从新赋值改变其值,也不能够在代码运行时从新声明。它必须被初始化为某个值。
常量的做用域规则与 let 块级做用域变量相同。若省略const关键字,则该标识符将被视为变量。
在同一做用域中,不能使用与变量名或函数名相同的名字来命名常量。
然而,对象属性被赋值为常量是不受保护的,因此下面的语句执行时不会产生错误。
const MY_OBJECT = {"key": "value"}; MY_OBJECT.key = "otherValue";
一样的,数组的被定义为常量也是不受保护的,因此下面的语句执行时也不会产生错误。
const MY_ARRAY = ['HTML','CSS']; MY_ARRAY.push('JAVASCRIPT'); console.log(MY_ARRAY); //logs ['HTML','CSS','JAVASCRIPT'];
译注:字面量是由语法表达式定义的常量;或,经过由必定字词组成的语词表达式定义的常量
在JavaScript中,你可使用各类字面量。这些字面量是脚本中按字面意思给出的固定的值,而不是变量。(译注:字面量是常量,其值是固定的,并且在程序脚本运行中不可更改
数组字面值是一个封闭在方括号对([])中的包含有零个或多个表达式的列表,其中每一个表达式表明数组的一个元素。当你使用数组字面值建立一个数组时,该数组将会以指定的值做为其元素进行初始化,而其长度被设定为元素的个数。
下面的示例用3个元素生成数组coffees,它的长度是3。
var coffees = ["French Roast", "Colombian", "Kona"]; var a=[3]; console.log(a.length); // 1 console.log(a[0]); // 3 //注意 这里的数组字面值也是一种对象初始化器。
若在顶层(全局)脚本里用字面值建立数组,JavaScript语言将会在每次对包含该数组字面值的表达式求值时解释该数组。另外一方面,在函数中使用的数组,将在每次调用函数时都会被建立一次。
数组字面值同时也是数组对象。有关数组对象的详情请参见数组对象一文。
数组字面值中的多余逗号
(译注:声明时)你没必要列举数组字面值中的全部元素。若你在同一行中连写两个逗号(,),数组中就会产生一个没有被指定的元素,其初始值是undefined。如下示例建立了一个名为fish的数组:
var fish = ["Lion", , "Angel"];
在这个数组中,有两个已被赋值的元素,和一个空元素(fish[0]是"Lion",fish[1]是undefined,而fish[2]是"Angel";译注:此时数组的长度属性fish.length是3)。
若是你在元素列表的尾部添加了一个逗号,它将会被忽略。在下面的例子中,数组的长度是3,并不存在myList[3]这个元素(译注:这是指数组的第4个元素噢,做者是在帮你们复习数组元素的排序命名方法)。元素列表中其它全部的逗号都表示一个新元素(的开始)。
注意:尾部的逗号在早期版本的浏览器中会产生错误,于是编程时的最佳实践方式就是移除它们。
(译注:而“现代”的浏览器彷佛鼓励这种方式,这也很好解释缘由。尾部逗号能够减小向数组的最后添加元素时,由于忘记为这最后一个元素加逗号 所形成的错误。)
var myList = ['home', , 'school', ];
在下面的例子中,数组的长度是4,元素myList[0]和myList[2]缺失(译注:没被赋值,于是是undefined)。
var myList = [ , 'home', , 'school'];
再看一个例子。在这里,该数组的长度是4,元素myList[1]和myList[3]被漏掉了。(可是)只有最后的那个逗号被忽略。
var myList = ['home', , 'school', , ];
理解多余的逗号(在脚本运行时会被如何处理)的含义,对于从语言层面理解JavaScript是十分重要的。可是,在你本身写代码时:显式地将缺失的元素声明为undefined,将大大提升你的代码的清晰度和可维护性。
布尔类型有两种字面量:true和false。
不要混淆做为布尔对象的真和假与布尔类型的原始值true和false。布尔对象是原始布尔数据类型的一个包装器
整数能够用十进制(基数为10)、十六进制(基数为16)、八进制(基数为8)以及二进制(基数为2)表示。
严格模式下,八进制整数字面量必须以0o或0O开头,而不能以0开头。
整数字面量举例:
0, 117 and -345 (十进制, 基数为10)
015, 0001 and -0o77 (八进制, 基数为8)
0x1123, 0x00111 and -0xF1A7 (十六进制, 基数为16或"hex")
0b11, 0b0011 and -0b11 (二进制, 基数为2)
对象字面值是封闭在花括号对({})中的一个对象的零个或多个"属性名-值"对的(元素)列表。你不能在一条语句的开头就使用对象字面值,这将致使错误或产生超出预料的行为, 由于此时左花括号({)会被认为是一个语句块的起始符号。(译者:这 里须要对语句statement、块block等基本名词的解释)
如下是一个对象字面值的例子。对象car的第一个元素(译注:即一个属性/值对)定义了属性myCar;第二个元素,属性getCar,引用了一个函数(即CarTypes("Honda"));第三个元素,属性special,使用了一个已有的变量(即Sales)。
var Sales = "Toyota"; function CarTypes(name) { return (name === "Honda") ? name : "Sorry, we don't sell " + name + "." ; } var car = { myCar: "Saturn", getCar: CarTypes("Honda"), special: Sales }; console.log(car.myCar); // Saturn console.log(car.getCar); // Honda console.log(car.special); // Toyota
更进一步的,你可使用数字或字符串字面值做为属性的名字,或者在另外一个字面值内嵌套上一个字面值。以下的示例中使用了这些可选项。
var car = { manyCars: {a: "Saab", "b": "Jeep"}, 7: "Mazda" }; console.log(car.manyCars.b); // Jeep console.log(car[7]); // Mazda
对象属性名字能够是任意字符串,包括空串。若是对象属性名字不是合法的javascript标识符,它必须用""包裹。属性的名字不合法,那么便不能用.访问属性值,而是经过类数组标记("[]")访问和赋值。
var unusualPropertyNames = { "": "An empty string", "!": "Bang!" } console.log(unusualPropertyNames.""); // 语法错误: Unexpected string console.log(unusualPropertyNames[""]); // An empty string console.log(unusualPropertyNames.!); // 语法错误: Unexpected token ! console.log(unusualPropertyNames["!"]); // Bang!
字符串字面量是由双引号(")对或单引号(')括起来的零个或多个字符。字符串被限定在同种引号之间;也即,必须是成对单引号或成对双引号。下面的例子都是字符串字面值:
"foo" 'bar' "1234" "one line \n another line" "John's cat"
你能够在字符串字面值上使用字符串对象的全部方法——JavaScript会自动将字符串字面值转换为一个临时字符串对象,调用该方法,而后废弃掉那个临时的字符串对象。你也能用对字符串字面值使用相似String.length的属性:
console.log("John's cat".length) // 将打印字符串中的字符个数(包括空格) // 结果为:10
在ES2015中,还提供了一种模板字符串(template literals),模板字符串提供了一些语法糖来帮你构造字符串。这与Perl、Python还有其余语言中的字符串插值(string interpolation)的特性很是类似。除此以外,你能够在经过模板字符串前添加一个tag来自定义模板字符串的解析过程,这能够用来防止注入攻击,或者用来创建基于字符串的高级数据抽象。
// Basic literal string creation `In JavaScript '\n' is a line-feed.` // Multiline strings `In JavaScript this is not legal.` // String interpolation var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?` // Construct an HTTP request prefix is used to interpret the replacements and construction POST`http://foo.org/bar?a=${a}&b=${b} Content-Type: application/json X-Credentials: ${credentials} { "foo": ${foo}, "bar": ${bar}}`(myOnReadyStateChangeHandler);
除非有特别须要使用字符串对象,不然,你应当始终使用字符串字面值。
条件判断语句指的是根据指定的条件所返回的结果(真或假或其它预约义的),来执行特定的语句。JavaScript 支持两种条件判断语句:if...else和switch。
let iceCream = 'chocolate'; if (iceCream === 'chocolate') { alert('我最喜欢巧克力冰激淋了。'); } else { alert('可是巧克力才是个人最爱呀……'); }
你能够用 throw 语句抛出一个异常而且用 try...catch 语句捕获处理它。
从 ECMAScript 6 开始,JavaScript 增长了对 Promise 对象的支持,它容许你对延时和异步操做流进行控制。
Promise 对象有如下几种状态:
展示了 Promise 的工做流
function imgLoad(url) { return new Promise(function(resolve, reject) { var request = new XMLHttpRequest(); request.open('GET', url); request.responseType = 'blob'; request.onload = function() { if (request.status === 200) { resolve(request.response); } else { reject(Error('Image didn\'t load successfully; error code:' + request.statusText)); } }; request.onerror = function() { reject(Error('There was a network error.')); }; request.send(); }); }
JavaScript中提供了这些循环语句:
一个for循环会一直重复执行,直到指定的循环条件为fasle。 JavaScript的for循环和Java与C的for循环是很类似的。
for ([initialExpression]; [condition]; [incrementExpression])
statement
当一个for循环执行的时候,会发生如下事件:
<form name="selectForm"> <p> <label for="musicTypes">Choose some music types, then click the button below:</label> <select id="musicTypes" name="musicTypes" multiple="multiple"> <option selected="selected">R&B</option> <option>爵士</option> <option>布鲁斯</option> <option>新纪元</option> <option>古典</option> <option>歌剧</option> </select> </p> <p><input id="btn" type="button" value="选择了多少个选项?" /></p> </form> <script> function howMany(selectObject) { var numberSelected = 0; for (var i = 0; i < selectObject.options.length; i++) { if (selectObject.options[i].selected) { numberSelected++; } } return numberSelected; } var btn = document.getElementById("btn"); btn.addEventListener("click", function(){ alert('选择选项的数量是: ' + howMany(document.selectForm.musicTypes)) }); </script>
do...while 语句一直重复直到指定的条件求值获得假(false)。 一个 do...while 语句看起来像这样:
do statement while (condition);
statement 在检查条件以前会执行一次。要执行多条语句(语句块),要使用块语句 ({ ... }) 包括起来。 若是 condition 为真(true),statement 将再次执行。 在每一个执行的结尾会进行条件的检查。当 condition 为假(false),执行会中止而且把控制权交回给 do...while 后面的语句。
在下面的例子中, 这个 do 循环将至少重复一次而且一直重复直到 i 再也不小于 5。
do { i += 1; console.log(i); } while (i < 5);
一个 while 语句只要指定的条件求值为真(true)就会一直执行它的语句块。一个 while 语句看起来像这样:
while (condition)statement
若是这个条件变为假,循环里的 statement 将会中止执行并把控制权交回给 while 语句后面的代码。
条件检测会在每次 statement 执行以前发生。若是条件返回为真, statement 会被执行并紧接着再次测试条件。若是条件返回为假,执行将中止并把控制权交回给 while 后面的语句。
要执行多条语句(语句块),要使用块语句 ({ ... }) 包括起来。
例子 1
下面的 while 循环只要 n 小于 3就会一直执行:
var n = 0; var x = 0; while (n < 3) { n++; x += n; }
在每次循环里, n 会增长1并被加到 x 上。因此, x 和 n 的变化是:
在三次完成后, 条件 n < 3 结果再也不为真,因此循环终止了。
例子 2
避免无穷循环(无限循环)。保证循环的条件结果最终会变成假;不然,循环永远不会中止。下面这个 while 循环会永远执行由于条件永远不会变成假:
while (true) { console.log("Hello, world"); }
函数 用来封装可复用的功能。若是没有函数,一段特定的操做过程用几回就要重复写几回,而使用函数则只需写下函数名和一些简短的信息
浏览器内置函数和用户定义的函数
一个函数定义(也称为函数声明,或函数语句)由一系列的function关键字组成,依次为:
例如,如下的代码定义了一个简单的square函数:
function square(number) { return number * number; }
函数square使用了一个参数,叫做number。这个函数只有一个语句,它说明该函数将函数的参数(即number)自乘后返回。函数的return语句肯定了函数的返回值:
return number * number;
原始参数(好比一个具体的数字)被做为值传递给函数;值被传递给函数,若是被调用函数改变了这个参数的值,这样的改变不会影响到全局或调用函数。
若是你传递一个对象(即一个非原始值,例如Array或用户自定义的对象)做为参数,而函数改变了这个对象的属性,这样的改变对函数外部是可见的,以下面的例子所示:
function myFunc(theObject) { theObject.make = "Toyota"; } var mycar = {make: "Honda", model: "Accord", year: 1998}; var x, y; x = mycar.make; // x获取的值为 "Honda" myFunc(mycar); y = mycar.make; // y获取的值为 "Toyota" // (make属性被函数改变了)
虽然上面的函数声明在语法上是一个语句,但函数也能够由函数表达式建立。这样的函数能够是匿名的;它没必要有一个名称。例如,函数square也可这样来定义:
var square = function(number) { return number * number; }; var x = square(4); // x gets the value 16
然而,函数表达式也能够提供函数名,而且能够用于在函数内部代指其自己,或者在调试器堆栈跟踪中识别该函数:
var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)}; console.log(factorial(3));
当将函数做为参数传递给另外一个函数时,函数表达式很方便。下面的例子演示了一个叫map的函数如何被定义,然后使用一个表达式函数做为其第一个参数进行调用:
function map(f,a) { var result = [],i; //建立一个新数组 for (i = 0; i != a.length; i++) result[i] = f(a[i]); return result; }
下面的代码:
function map(f, a) { var result = []; // 建立一个数组 var i; // 声明一个值,用来循环 for (i = 0; i != a.length; i++) result[i] = f(a[i]); return result; } var f = function(x) { return x * x * x; } var numbers = [0,1, 2, 5,10]; var cube = map(f,numbers); console.log(cube);
返回 [0, 1, 8, 125, 1000]。
在 JavaScript 中,能够根据条件来定义一个函数。好比下面的代码,当num 等于 0 的时候才会定义 myFunc :
var myFunc; if (num == 0){ myFunc = function(theObject) { theObject.make = "Toyota" } }
除了上述的定义函数方法外,你也能够在运行时用 Function 构造器由一个字符串来建立一个函数 ,很像 eval() 函数。
当一个函数是一个对象的属性时,称之为方法。了解更多关于对象和方法的知识 使用对象。
调用函数节
定义一个函数并不会自动的执行它。定义了函数仅仅是赋予函数以名称并明确函数被调用时该作些什么。调用函数才会以给定的参数真正执行这些动做。例如,一旦你定义了函数square,你能够以下这样调用它:
square(5);
上述语句经过提供参数 5 来调用函数。函数执行完它的语句会返回值25。
函数必定要处于调用它们的域中,可是函数的声明能够被提高(出如今调用语句以后),以下例:
console.log(square(5)); /* ... */ function square(n) { return n*n }
函数域是指函数声明时的所在的地方,或者函数在顶层被声明时指整个程序。
提示:注意只有使用如上的语法形式(即 function funcName(){})才能够。而下面的代码是无效的。就是说,函数提高仅适用于函数声明,而不适用于函数表达式。
console.log(square); // square is hoisted with an initial value undefined. console.log(square(5)); // TypeError: square is not a function var square = function (n) { return n * n; }
函数的参数并不局限于字符串或数字。你也能够将整个对象传递给函数。函数 show_props(其定义参见 用对象编程)就是一个将对象做为参数的例子。
函数能够被递归,就是说函数能够调用其自己。例如,下面这个函数就是用递归计算阶乘:
function factorial(n){ if ((n == 0) || (n == 1)) return 1; else return (n * factorial(n - 1)); }
你能够计算1-5的阶乘以下:
var a, b, c, d, e; a = factorial(1); // 1赋值给a b = factorial(2); // 2赋值给b c = factorial(3); // 6赋值给c d = factorial(4); // 24赋值给d e = factorial(5); // 120赋值给e
还有其它的方式来调用函数。常见的一些情形是某些地方须要动态调用函数,或者函数的实参数量是变化的,或者调用函数的上下文须要指定为在运行时肯定的特定对象。显然,函数自己就是对象,所以这些对象也有方法(参考Function )。做为此中情形之一,apply()方法能够实现这些目的。
在函数内定义的变量不能在函数以外的任何地方访问,由于变量仅仅在该函数的域的内部有定义。相对应的,一个函数能够访问定义在其范围内的任何变量和函数。换言之,定义在全局域中的函数能够访问全部定义在全局域中的变量。在另外一个函数中定义的函数也能够访问在其父函数中定义的全部变量和父函数有权访问的任何其余变量。
// 下面的变量定义在全局做用域(global scope)中 var num1 = 20, num2 = 3, name = "Chamahk"; // 本函数定义在全局做用域 function multiply() { return num1 * num2; } multiply(); // 返回 60 // 嵌套函数的例子 function getScore() { var num1 = 2, num2 = 3; function add() { return name + " scored " + (num1 + num2); } return add(); } getScore(); // 返回 "Chamahk scored 5"