ECMAScript6(基础入门)

简介 ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现

官方文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/A_re-introduction_to_JavaScript

介绍

数据类型

类型转换

转数字

parseInt:如果前几个字符是数字可以转成数字。

Number:将整体转成数字。

1
2
3
var num = '123abc';
console.log( parseInt(num) ); // 123
console.log( Number(num) ); // NaN (Not a Number)

转字符串

1
2
console.log( 100 + 100 + '100abc');        // 200100abc
console.log( '' + 100 + 100 + '100abc'); // 100100100abc

变量

定义变量:varlet

定义常量:const

var

可以定义 局部变量全局变量

1
2
3
4
5
var name;   // 全局变量

function abc() {
var age; // 局部变量
}

意外的全局变量。

如果在函数中定义变量时没有写 var 或者 let 那么变量会变成全局变量。

1
2
3
4
5
6
7
function abc() {
age = 10 // 全局变量
}

abc()

console.log( age ) // 10

let

可以定义块级变量局部变量全局变量

let 可以定义块级变量(只在大括号中生效的变量)

示例1:let 和 var 的区别

1
2
3
4
5
6
7
8
9
10
for(let i=0;i<10;i++) {

}
console.log( i ) // undefined


for(var i=0;i<10;i++) {

}
console.log( i ) // 10

示例2:使用 var 时无论,调用数组中第几个函数,输出的结果都是10

1
2
3
4
5
6
7
8
9
var funs = []
for(var i=0;i<10;i++) {
funs[i] = function() {
console.log( i )
}
}
funs[3](); //10
funs[4](); //10
funs[5](); //10

使用 let 时的效果:

1
2
3
4
5
6
7
8
9
var funs = []
for(let i=0;i<10;i++) {
funs[i] = function() {
console.log( i )
}
}
funs[3](); // 3
funs[4](); // 4
funs[5](); // 5

短路运算符

我们可以使用 &&|| 实现短路操作。

&& : 连接多个语句,当语句1为真是执行语句2.

||:连接多个语句,当语句1为假时执行语句2.

可以用短路运算符来实现一些简写,比如,有时我们获取到一个变量,不知道它的值是什么,但我们希望无论如何它都是一个数字,至少是个0,我们就可以写:

1
2
3
4
5
// 如果 a 是假就得到0
var a = a || 0

// 如果 o 存在就调用 o.name
o && o.name()

循环

除了 while、do…while、for 之外,我们还可以使用:

  1. 在循环数组时,我们可以使用 for...offor...inforEach
  2. 循环对象时,我们可以使用 for...in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let arr = ['a','b','c','d','e']

// 循环得到值
for(let i of arr) {
console.log(i) // abcde
}

// 循环得到下标
for(let i in arr) {
console.log(i) // 01234
}

// 循环得到下标和值
arr.forEach((v,k)=>{
console.log(k,v) // 0a1b2c3d4e
})

函数

arguments

在函数中可以使用 arguments 获取函数在调用时的参数数组。

1
2
3
4
5
6
function abc() {
console.log( arguments.length ) // 2 , 获取参数的数量
console.log( arguments[0] ) // tom ,第一个参数值
console.log( arguments[1] ) // jack ,第二个参数值
}
abc('tom','jack')

匿名函数

匿名函数可以用来隔绝一段作用域。

有时我们需要在一个现有的项目中添加新的代码,这时当我们添加变量、函数时就有可能出现和原来系统中变量名、函数名冲突的情况,为了避免我们的代码和原代码中出现命名冲突的情况,我们就可以把我们的代码放到一个匿名函数中,然后调用这个匿名函数,这样我们所写的代码都只在这个匿名函数内生效,就不会影响其它代码了。

有两种写法,来调用匿名函数得到一段独立的作用域:

1
2
3
4
5
6
7
8
9
10
11
(function(){

// 这里的代码不影响外面的作用域

})()

!function(){

// 这里的代码不影响外面的作用域

}()

箭头函数

箭头函数是匿名函数的一种简写方式。

1
2
3
4
5
6
7
8
9
// 普通函数
setTimeout(function(){
console.log( 'hello' )
}, 1000)

// 箭头函数
setTimeout(()=>{
console.log( 'hello' )
}, 1000)

箭头函数和普通函数中的 this 是不同的。

比如以下代码运行的结果是不同的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var age = 20

function abc() {
this.age = 10

setTimeout(function(){
console.log( this.age ) // 20
}, 1000)

setTimeout(()=>{
console.log( this.age ) // 10
}, 1000)
}

abc()

call/apply

call 和 apply 的功能相同:使一个函数归属于一个对象来调用

1
2
3
4
5
6
7
8
function abc() {
console.log( this.age )
}
var o = {
age:10
}
abc(); // 独立调用时 this.age 不存在
abc.call(o); // 10 , abc 函数归属于 o 这个对象来调用,这时 this.age 指的就是 o 对象中的 age。

call 和 apply 的区别是参数形式不同:

1
2
3
4
5
6
7
8
function abc(a,b) {
console.log( this.age )
}
var o = {
age:10
}
abc.call(o, 10, 20); // 10 , 20 对应 a, b 两个参数
abc.apply(o, [10,20]); // [10,20] 数组中的对应 a,b 两个参数

严格模式

以下代码在严格模式下会报错:

1
2
3
4
5
6
7
'use strict'      // 严格模式

function abc() {
age = 'tom' // 报错
}

abc();

闭包

以下代码就是一个闭包,它制作了一个 id 变量,这个变量不会马上销毁,会一直存在,并且不能被外部随意修改,只能通过闭包内修改。

1
2
3
4
5
6
7
8
9
10
11
12
// 定义闭包
function getId() {
var id = 0;
return function() {
return id++;
}
}
// 获取一个自增的ID
var id = getId()
console.log( id() ) //0
console.log( id() ) //1
console.log( id() ) //2

内存泄漏

循环引用

当 DOM 对象和 JS 对象互相引用时,会导致两个对象在该销毁时都无法销毁。

比如下面的 el 和 o 这两个对象都是局部变量,在函数执行之后就应该销毁,但因为两个对象互相引用,所以导致函数执行之后也无法销毁:

原型链

原型链:所有的对象都会继承自一个原型对象,这个原型对象又会继承自一个原型对象,一层一层直到最顶层继承自 null,这样的链就是原型链。

构造函数:生成对象的函数。

访问原型对象

对象中通过 __proto__ 访问原型对象。

构造函数中通过 prototype 访问原型对象。

基于原型链的面向对象

JS 在实现面向对象编程时传统的方法是基于原型链+构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 定义一个类
function Person(name, age) {
this.name = name
this.age = age
}

// 在原型对象上添加方法
Person.prototype.setName = function(name) {
this.name = name
}
Person.prototype.getName = function() {
return this.name
}

// 生成对象
var p1 = new Person('tom',10)
console.log( p1.getName() ); // tom

JS 中实现面向对象编程的原则:

  1. 属性定义到构造函数中

  2. 方法定义到原型对象上(避免每个对象都拷贝一份函数)

ES6中的面向对象

ES6 中提供了 class 可以实现面向对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person
{
// 构造函数
constructor(name,age) {
this.name = name
this.age = age
}

getName() {
return this.name
}

setName(name) {
this.name = name
}
}

var p1 = new Person('tom',10)
console.log( p1.getName() ) // tom

继承

在原型统的 JS 中我们使用原型对象实现继承,在 ES6 中我们可以使用 extends 实现类的继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person
{
// 构造函数
constructor(name,age) {
this.name = name
this.age = age
}

getName() {
return this.name
}

setName(name) {
this.name = name
}
}
// 继承自 Person
class Boy extends Person {

}

super

在继承时,如果子类中定义了构造函数,那么在构造函数中必须要调用 super 方法来调用父类的构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Person
{
// 构造函数
constructor(name,age) {
this.name = name
this.age = age
}

getName() {
return this.name
}

setName(name) {
this.name = name
}
}
// 继承自 Person
class Boy extends Person {
constructor(name, age) {
// 调用父类的构造函数
super(name, age)
}
}