@linux1s1s
2017-01-23T17:05:16.000000Z
字数 4715
阅读 1477
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) // 1
f(1) // 1
f() // undefined
f.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() // 5
inc() // 6
inc() // 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);
}());
上面代码中,写法二比写法一更好,因为完全避免了污染全局变量。