[关闭]
@linux1s1s 2017-01-23T09:05:16.000000Z 字数 4715 阅读 852

Base Time-JavaScript

Base JavaScript 2017-01


语法

动态类型

  1. var a = 1;
  2. a = 'hello';

变量提升

  1. console.log(a);
  2. var a = 1;
  1. console.log(b);
  2. 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)。也就是说,区块中的变量与区块外的变量,属于同一个作用域

If结构

  1. var x = 1;
  2. var y = 2;
  3. if (x = y) {
  4. console.log(x);
  5. }

上面代码的原意是,当x等于y的时候,才执行相关语句。但是,不小心将“严格相等运算符”写成“赋值表达式”,结果变成了将y赋值给x,然后条件就变成了,变量x的值(等于2)自动转为布尔值以后,判断其是否为true。

此处输入图片的描述

优先采用“严格相等运算符”(===),而不是“相等运算符”(==)

对于严格相等运算符可以进一步看switch语句

  1. var x = 1;
  2. switch (x) {
  3. case true:
  4. console.log('x发生类型转换');
  5. default:
  6. console.log('x没有发生类型转换');
  7. }

此处输入图片的描述

需要注意的是,switch语句后面的表达式与case语句后面的表示式,在比较运行结果时,采用的是严格相等运算符(===),而不是相等运算符(==),这意味着比较时不会发生类型转换。

三目运算符

  1. (condition) ? expr1 : expr2

标签(label)

JavaScript语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签的格式如下。

此处输入图片的描述

基本类型

primitive type

数值(number):整数和小数(比如1和3.14)
字符串(string):字符组成的文本(比如”Hello World”)
布尔值(boolean):true(真)和false(假)两个特定值

undefined&&null

undefined:表示“未定义”或不存在,即此处目前没有任何值
null:表示空缺,即此处应该有一个值,但目前为空

complex type

狭义的对象(object)

狭义的对象和数组是两种不同的数据组合方式,而函数其实是处理数据的方法。JavaScript把函数当成一种数据类型,可以像其他类型的数据一样,进行赋值和传递,这为编程带来了很大的灵活性,体现了JavaScript作为“函数式语言”的本质。

数组(array)
  1. var arr = [
  2. {a: 1},
  3. [1, 2, 3],
  4. function() {return true;}
  5. ];
  6. arr[0] // Object {a: 1}
  7. arr[1] // [1, 2, 3]
  8. arr[2] // function (){return true;}
  1. var a = [];
  2. a['1000'] = 'abc';
  3. a[1000] // 'abc'
  4. a[1.00] = 6;
  5. a[1] // 6

上面代码表明,由于字符串“1000”和浮点数1.00都可以转换为整数,所以视同为整数键赋值。

  1. var a = [1, 2, 3];
  2. a.foo = true;
  3. for (var key in a) {
  4. console.log(key);
  5. }

此处输入图片的描述

上面代码在遍历数组时,也遍历到了非整数键foo。

范例:

  1. var a = [1, 2, 3];
  2. // for循环
  3. for(var i = 0; i < a.length; i++) {
  4. console.log(a[i]);
  5. }
  6. // while循环
  7. var i = 0;
  8. while (i < a.length) {
  9. console.log(a[i]);
  10. i++;
  11. }
  12. var l = a.length;
  13. while (l--) {
  14. console.log(a[l]);
  15. }
函数(function)
  1. var foo = new Function(
  2. 'return "hello world"'
  3. );
  4. // 等同于
  5. function foo() {
  6. return 'hello world';
  7. }

总的来说,这种声明函数的方式非常不直观,几乎无人使用。

由于函数与其他数据类型地位平等,所以在JavaScript语言中又称函数为第一等公民。

栗子:

  1. function add(x, y) {
  2. return x + y;
  3. }
  4. // 将函数赋值给一个变量
  5. var operator = add;
  6. // 将函数作为参数和返回值
  7. function a(op){
  8. return op;
  9. }
  10. a(add)(1, 1)
  11. // 2

验证:
此处输入图片的描述

  1. var a = 1;
  2. var x = function () {
  3. console.log(a);
  4. };
  5. function f() {
  6. var a = 2;
  7. x();
  8. }
  9. f() // 1

上面代码中,函数x是在函数f的外部声明的,所以它的作用域绑定外层,内部变量a不会到函数f体内取值,所以输出1,而不是2。

如果修改var a = 2; ===>> a = 2;结果是什么样子?

此处输入图片的描述

  1. function f(a, b) {
  2. return a;
  3. }
  4. f(1, 2, 3) // 1
  5. f(1) // 1
  6. f() // undefined
  7. f.length // 2

此处输入图片的描述

由于JavaScript允许函数有不定数目的参数,所以我们需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。

arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments1就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。

  1. var f = function(one) {
  2. console.log(arguments[0]);
  3. console.log(arguments[1]);
  4. console.log(arguments[2]);
  5. }
  6. f(1, 2, 3)
  7. // 1
  8. // 2
  9. // 3

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
要理解闭包,首先必须理解变量作用域。前面提到,JavaScript有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。

  • 闭包的来源:
    如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。
  1. function f1() {
  2. var n = 999;
  3. function f2() {
  4.   console.log(n); // 999
  5. }
  6. }

上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是JavaScript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

  1. function f1() {
  2. var n = 999;
  3. function f2() {
  4. console.log(n);
  5. }
  6. return f2;
  7. }
  8. var result = f1();
  9. result();

闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

  • 作用1:闭包可以使得它诞生环境一直存在。
  1. function createIncrementor(start) {
  2. return function () {
  3. return start++;
  4. };
  5. }
  6. var inc = createIncrementor(5);
  7. inc() // 5
  8. inc() // 6
  9. inc() // 7
  • 作用2:封装对象的私有属性和私有方法。
  1. function Person(name) {
  2. var _age;
  3. function setAge(n) {
  4. _age = n;
  5. }
  6. function getAge() {
  7. return _age;
  8. }
  9. return {
  10. name: name,
  11. getAge: getAge,
  12. setAge: setAge
  13. };
  14. }
  15. var p1 = Person('张三');
  16. p1.setAge(25);
  17. p1.getAge()

Immediately-Invoked Function Expression (立即调用的函数表达式)

  1. (function(){ /* code */ }());
  2. // 或者
  3. (function(){ /* code */ })();

通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

  1. // 写法一
  2. var tmp = newData;
  3. processData(tmp);
  4. storeData(tmp);
  5. // 写法二
  6. (function (){
  7. var tmp = newData;
  8. processData(tmp);
  9. storeData(tmp);
  10. }());

上面代码中,写法二比写法一更好,因为完全避免了污染全局变量。

参考:JavaScript 教程
JavaScript 标准参考教程(alpha)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注