@linux1s1s
2017-01-23T09:05:16.000000Z
字数 4715
阅读 1748
Base JavaScript 2017-01
var a = 1;a = 'hello';
console.log(a);var a = 1;
console.log(b);b = 1;
代码段① 不会报错,代码段② 直接抛异常

JavaScript有一些保留字,不能用作标识符:arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。
JavaScript使用大括号,将多个相关的语句组合在一起,称为“区块”(block)。
与大多数编程语言不一样,JavaScript的区块不构成单独的作用域(scope)。也就是说,区块中的变量与区块外的变量,属于同一个作用域。
var x = 1;var y = 2;if (x = y) {console.log(x);}
上面代码的原意是,当x等于y的时候,才执行相关语句。但是,不小心将“严格相等运算符”写成“赋值表达式”,结果变成了将y赋值给x,然后条件就变成了,变量x的值(等于2)自动转为布尔值以后,判断其是否为true。

优先采用“严格相等运算符”(===),而不是“相等运算符”(==)
对于严格相等运算符可以进一步看switch语句
var x = 1;switch (x) {case true:console.log('x发生类型转换');default:console.log('x没有发生类型转换');}

需要注意的是,switch语句后面的表达式与case语句后面的表示式,在比较运行结果时,采用的是严格相等运算符(===),而不是相等运算符(==),这意味着比较时不会发生类型转换。
(condition) ? expr1 : expr2
JavaScript语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签的格式如下。

数值(number):整数和小数(比如1和3.14)
字符串(string):字符组成的文本(比如”Hello World”)
布尔值(boolean):true(真)和false(假)两个特定值
undefined:表示“未定义”或不存在,即此处目前没有任何值
null:表示空缺,即此处应该有一个值,但目前为空
狭义的对象和数组是两种不同的数据组合方式,而函数其实是处理数据的方法。JavaScript把函数当成一种数据类型,可以像其他类型的数据一样,进行赋值和传递,这为编程带来了很大的灵活性,体现了JavaScript作为“函数式语言”的本质。
var arr = [{a: 1},[1, 2, 3],function() {return true;}];arr[0] // Object {a: 1}arr[1] // [1, 2, 3]arr[2] // function (){return true;}
var a = [];a['1000'] = 'abc';a[1000] // 'abc'a[1.00] = 6;a[1] // 6
上面代码表明,由于字符串“1000”和浮点数1.00都可以转换为整数,所以视同为整数键赋值。
var a = [1, 2, 3];a.foo = true;for (var key in a) {console.log(key);}

上面代码在遍历数组时,也遍历到了非整数键foo。
范例:
var a = [1, 2, 3];// for循环for(var i = 0; i < a.length; i++) {console.log(a[i]);}// while循环var i = 0;while (i < a.length) {console.log(a[i]);i++;}var l = a.length;while (l--) {console.log(a[l]);}
var foo = new Function('return "hello world"');// 等同于function foo() {return 'hello world';}
总的来说,这种声明函数的方式非常不直观,几乎无人使用。
由于函数与其他数据类型地位平等,所以在JavaScript语言中又称函数为第一等公民。
栗子:
function add(x, y) {return x + y;}// 将函数赋值给一个变量var operator = add;// 将函数作为参数和返回值function a(op){return op;}a(add)(1, 1)// 2
验证:

var a = 1;var x = function () {console.log(a);};function f() {var a = 2;x();}f() // 1
上面代码中,函数x是在函数f的外部声明的,所以它的作用域绑定外层,内部变量a不会到函数f体内取值,所以输出1,而不是2。
如果修改var a = 2; ===>> a = 2;结果是什么样子?

function f(a, b) {return a;}f(1, 2, 3) // 1f(1) // 1f() // undefinedf.length // 2

但是,如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。
arguments对象
由于JavaScript允许函数有不定数目的参数,所以我们需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。
arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments1就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
var f = function(one) {console.log(arguments[0]);console.log(arguments[1]);console.log(arguments[2]);}f(1, 2, 3)// 1// 2// 3
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
要理解闭包,首先必须理解变量作用域。前面提到,JavaScript有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。
- 闭包的来源:
如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。
function f1() {var n = 999;function f2() {console.log(n); // 999}}
上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是JavaScript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
function f1() {var n = 999;function f2() {console.log(n);}return f2;}var result = f1();result();
闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
- 作用1:闭包可以使得它诞生环境一直存在。
function createIncrementor(start) {return function () {return start++;};}var inc = createIncrementor(5);inc() // 5inc() // 6inc() // 7
- 作用2:封装对象的私有属性和私有方法。
function Person(name) {var _age;function setAge(n) {_age = n;}function getAge() {return _age;}return {name: name,getAge: getAge,setAge: setAge};}var p1 = Person('张三');p1.setAge(25);p1.getAge()
Immediately-Invoked Function Expression (立即调用的函数表达式)
(function(){ /* code */ }());// 或者(function(){ /* code */ })();
通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
// 写法一var tmp = newData;processData(tmp);storeData(tmp);// 写法二(function (){var tmp = newData;processData(tmp);storeData(tmp);}());
上面代码中,写法二比写法一更好,因为完全避免了污染全局变量。
