ES5
ES5
数据类型
六种基本数据类型:数字Number、字符串String、未定义Undefined、空Null、布尔值Boolean、Symbol(ES6)
三种引用类型:对象Object、数组Array、函数Function
有几点需要注意的:
==和===:使用==时,会自动转换符号两边的数据类型再进行比较,容易出错。使用===时,不会自动转换数据类型,所以也对数据类型进行了比较。
null和undefined:null表示没有这个值,而undefined表示应该有这个值但没有定义。
undefined == null //true undefined === null //false
NaN和Infinite:NaN表示不是一个数值,它不与任何值相等,Infinite表示超出了Number类型能表示的最大范围
typeof和instanceof:typeof可以把数据类型作为字符串返回,但对于array、null和对象,typeof一律返回object。instanceof用于判断一个变量是否是某个对象的实例。
函数级作用域
在 JavaScript 中, 对象和函数同样也是变量,作用域为可访问变量,对象,函数的集合。
局部变量和局部作用域:
变量在函数内声明,为局部变量,局部作用域只能在函数内部访问。(函数参数只在函数内起作用,是局部变量)
全局变量和全局作用域:
变量在函数外定义,即为全局变量。全局变量有全局作用域, 网页中所有脚本和函数均可使用。
变量生命周期:
JavaScript 变量生命周期在它声明时初始化。局部变量在函数执行完毕后销毁。全局变量在页面关闭后销毁。
注意:在 HTML 中, 全局变量是 window 对象: 所有数据变量都属于 window 对象。
闭包
作用域的好处是内部函数可以访问定义它们的外部函数的参数和变量(除了this和arguements),不可逆。
变量声明时如果不使用 var 关键字,那么它就是一个全局变量,即便它在函数内定义。
出于种种原因,我们有时候需要得到函数内的局部变量。但是,正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。
function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999
在上述代码中,f2其实就是闭包。本质上,闭包就是能够读取其他函数内部变量的函数。
函数调用
调用一个函数将暂停当前函数的执行,传递控制权和参数给新函数,除了声明时定义的形式参数,每个函数接收两个附加的参数:this和arguements。参数this在面向对象编程中非常重要,它的取值取决于调用的模式,在JavaScript中一共有四种调用模式:方法调用模式、函数调用模式、构造器调用模式和apply调用模式。
方法调用模式:
当一个函数被保存为一个对象的属性时,我们称它为一个方法。
方法可以使用this去访问对象。
var myObject={ value:0; increment:function(inc){ this.value+=typeof inc==='number'?inc:1; } }; myObject.increment(); document.writeln(myObject.value);
函数调用模式:
当一个函数并非一个对象的属性时,那么它被当作一个函数来调用。当函数以此模式调用时,this被绑定到全局对象。
当内部函数被调用时,this应该仍然绑定到外部函数的this变量,所以我们给该方法定义一个变量并给它赋值为this,那么内部函数就可以通过那个变量访问到this,不过ES6中箭头函数的出现,有效的解决了this的指向问题。
构造器调用模式
JavaScript是一门基于原型继承的语言,这意味着对象可以直接从其他对象继承属性,这偏离了当今编程语言的主流,当今大多数语言都是基于类的语言,尽管原型继承有着强大的表现力,但它并不被广泛理解,所以JavaScript提供了一套和基于类的语言类似的对象构建语法。(ES6中新增了类,实质就是原型的语法糖)
构造函数中 this 关键字没有任何的值。this 的值在函数调用实例化对象(new object)时创建。
// 构造函数: function myFunction(arg1, arg2) { this.firstName = arg1; this.lastName = arg2; } // This creates a new object var x = new myFunction("John","Doe"); x.firstName; // 返回 "John"
apply调用模式
因为JavaScript是一门函数式的面向对象编程语言,所以函数可以拥有方法。
call() 和 apply() 是预定义的函数方法,通过 这两个方法你可以设置 this 的值, 且作为已存在对象的新方法调用。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身
function myFunction(a, b) { return a * b; } myObject = myFunction.call(myObject, 10, 2); // 返回 20
function myFunction(a, b) { return a * b; } myArray = [10, 2]; myObject = myFunction.apply(myObject, myArray); // 返回 20
两个方法都使用了对象本身作为第一个参数。 两者的区别在于第二个参数: apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。
在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。
在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。
高阶函数
高阶函数源自于函数式编程,是函数式编程的基本技术。函数可以作为参数传递,也可以作为返回值返回。
这里还有篇蛮详细的文章,戳我~
模块加载
四种规范:
AMD (require.js)
CMD (sea.js)
CommonJS (node.js)
ES6 模块 (原生JS)
因为AMD和CMD规范已经逐渐被淘汰,所以我们只来学习CommonJS和ES6模块。
CommonJS
Node.js是commonJS规范的主要实践者,它有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global。
实际使用时,用module.exports定义当前模块对外输出的接口(不推荐直接用exports),用require加载模块。
CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。这是因为服务器中模块都在磁盘中,读取非常快。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式(因为网络原因),因此浏览器端一般采用 AMD 规范或者ES6加载。
ES6模块
模块功能主要由两个命令构成:export和import,export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
export default命令,为模块指定默认输出)
// 写法一 export var m = 1; // 写法二 var m = 1; export {m}; // 写法三 var n = 1; export {n as m};
// 报错 export 1; // 报错 var m = 1; export m;
如果想为输入的变量重新取一个名字,import
命令要使用as
关键字,将输入的变量重命名。
import { lastName as surname } from './profile.js';
import
命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
import
后面的from
指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js
后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。
ES6模块详细见这里
两者差异见这里