接近两万字的ES6理解笔记

前言

ECMAScript 是 JS 的语言标准。而 ES6 是新的 JS 语法标准。

PS:严格来说,ECMAScript 还包括其他很多语言的语言标准。

很多人在做业务选型的时候,会倾向于选jQuery。其实jQuery的语法是偏向于ES3的。而现在主流的框架 Vue.js 和React.js的语法,是用的ES6。

ES6中增加了很多功能上的不足。比如:常量、作用域、对象代理、异步处理、类、继承等。这些在ES5中想实现,比较复杂,但是ES6对它们进行了封装。


ECMAScript 发展历史

版本 发布时间 发布内容
第一版 1997年 制定的基本语法(1995年ECMAScript 诞生时间)
第二版 1998年 较小改动
第三版 1999年 引入正则、异常处理、格 式化输出等。IE 开始支持
第 4 版 2007 年 过于激进,未发布
第 5 版 2009 年 引入严格模式、JSON,扩 展对象、数组、原型、字 符串、日期方法
第 6 版 2015 年 模块化、面向对象语法、 Promise、箭头函数、let、 const、数组解构赋值等等
第 7 版 2016 年 幂运算符、数组扩展、 Async/await 关键字
第 9版 2017 年 Async/await、字符串扩展
第 10 版 2018 年 对象解构赋值、正则扩展
第 11 版 2019 年 扩展对象、数组方法

ES6新增的变量声明

ES6 新增了 let 和 const 来声明变量

  • var:ES5 和 ES6中,定义全局变量(是variable的简写)。

  • let:定义局部变量,替代 var。

  • const:定义常量(定义后,不可修改)。


let关键字

使用 let 声明的变量有几个特点:

不允许重复声明
let不允许在相同作用域内,重复声明同一个变量。

let test = 10;
let test = 20;
// 报错:Uncaught SyntaxError: Identifier 'test' has already been declared

块儿级作用域
ES5 只有全局作用域 和 函数作用域,没有块级作用域,这就会造成一些问题,如:
用来计数的循环变量泄露为全局变量。

for (var i = 0; i < 10; i++) {
     
    console.log('循环体中:' + i); 
    // 每循环一次,就会在 { } 所在的块级作用域中,重新定义一个新的 i
}

console.log('循环体外:' + i);

变量 i 只是用来控制循环,但是循环结束后,并没有消失,并且泄露给了全局变量

再来看下ES6 的 let 声明

var a = 2;
{
     
    let a = 10;
}
console.log(a); 

上方代码的输出结果为 2。用 let 声明的变量,只在局部(块级作用域内)起作用。

使用 let 解决 var 在for循环时定义造成的变量泄露到全局行为

for (let i = 0; i < 10; i++) {
     
    console.log('循环体中:' + i);
}

console.log('循环体外:' + i);
  • 上方代码的最后一行无法打印结果,也就是说打印会报错。因为用 let 定义的变量 i,只在{ }这个块级作用域里生效。
  • 使用let可以减少var声明带来的污染全局空间

不存在变量提升
使用 ver 会发生 “变量提升现象”,就是在变量声明之前就使用,值为 undefined ,为了纠正这种奇怪的现象, let 命令改变了语法的行为,使得声明的变量一定要在声明之后使用,否则就会报错。

console.log(a);	// var 声明的会变量提升 结果为undefined
var a = 10;

console.log(b); // 报错:ncaught ReferenceError: b is not defined
let b = 20; 

暂时性死区
只要在 {} 内存在 let 命令 他所在的变量 就 “绑定” 在这个区域了,不再受外部的影响

var temp = 123;
{
     
    temp = 'abc';
    let temp;
}
// 报错:temp is not defined

上面的代码中虽然存在全局变量,但是在 {} 内使用了 let 然后就这个区域就是一个块级作用域。在这个作用域内 temp 变量还没有声明就使用了就会报错:temp 没有定义

let 的出现就是为了替代var会出现的一些怪异现象 所以以后声明变量使用 let 就对了


const关键字

const 关键字用来声明常量,const 声明有以下特点

// 声明常量
const STUDY = "好好学习,天天向上";

1.声明必须赋初始值

const A;
// 报错:Uncaught SyntaxError: Missing initializer in const declaration

2.一般常量使用大写(行业默认)

const NAME = '张三';
const AGE =  20;

3.常量的值不能修改

const HELLO = '你好';
HELLO = '大家好';
// 报错:Uncaught TypeError: Assignment to constant variable.

4.和 let 一样可以形成块级作用域

{
     
    const PLAPER = 'LWX';
} 
console.log(PLAPER); 
// 外部访问不到:Uncaught ReferenceError: PLAPER is not defined

5.对于数组和对象的元素修改,不算做对常量的修改,不会报错

const TEAM = ['UZI','MLXG','Ming','Letme'];
TEAM.push('Meiko');

虽然值修改了,但是常量指向的地址没有发生改变,所以不会报错。

但是如果我们把TEAM的值修改了,那么就会报错,如:

const TEAM = ['UZI','MLXG','Ming','Letme'];
TEAM = 100;
// 报错:Uncaught SyntaxError:
//		 Identifier 'TEAM' has already been declared

总结: 以后声明数组或者对象使用const声明更加的稳妥,避免误操作,修改了数据值。避免潜在的问题


变量的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称 为解构赋值。本质上,解构赋值就是模式匹配。解构赋值在实际开发中可以大量减少我们的代码量,并且让程序结构更清晰


数组解构赋值

数组解构赋值允许我们按照 一 一 对应的关系从数组中提取值,然后在将值赋值给变量

举例:

// 以前的取值
let a = 10;
let b = 20;
let c = 30;

现在我们可以通过数组解构的方式进行赋值:

let [a, b, c] = [10,20,30];

取值也是一样

var arr = [10,20,30];
var [a,b,c] = arr;

解构例子:

let [,,third] = [1,2,3];
console.log(third);	//3

let [x, ,z] = [1,2,3];
console.log(x,z);	// 1 3

let [head, ...tail] = [1, 2, 3, 4];
console.log(head,tail); // 1 [2,3,4]

let [x, y, ...z] = ['a']; 
console.log(x,y,z); // x undefined []

数组结构赋值的特点

1.如果解构不成功 那么变量的值就等于undefined

let [a] = [];
let [bar,foo] = [1];
console.log(a); // undefined 未赋值
console.log(bar,foo);	//bar = 1,foo = undefined

2.不完全解构情况下 也可以进行解构赋值 即等号左右两边模式一样 但是只匹配到右边的一部分数据

let[x,y] = [1,2,3]
console.log(x,y);    // 1,2

let [x, y,z,e,f] = [1, 2, 3]; 
console.log(x,y,z,e,f); // 1 2 3  undefined undefined

3.如果等号右边不是数组(不是可遍历的结构) 那么就会报错

let [m] = 1;
let [m] = false;
let [m] = null;
let [m] = {
     };
console.log(m);
// 统统不是数组 报错:Uncaught TypeError: false/null... is not iterable

4.在解构赋值时,是允许使用默认值的,但是要启动默认值 这个变量的值就必须严格等于 undefined。举例如下:

let[a,b = "abc"] = [1,2]
console.log(a,b);   // 1,2

let[a,b = "abc"] = [1]
console.log(a,b);   // 1,"abc"

let [a, b = "abc"] = [1,null];
console.log(a,b);   // 1 "null"

let [a, b = "abc"] = [1,undefined];
console.log(a,b);   // 1 "abc"

let [a, b = "abc"] = [1,""];
console.log(a,b);   // 1 ""

5.默认值可以使用其他变量 但是前提是赋值的这个变量必须是提前声明过的

let d = 3;
let [x = d] = [];
console.log(x); // 3

let [x = 2, y = x] = [];
console.log(x,y);   // 2,2

let [x = y, y = 2] = [];
console.log(x,y);   // 报错  因为x = y时 y还没有声明
// ||
let x = y;
let y = 2;

let [x = 2,y = x] = [8];
console.log(x,y);   // 8,8

对象的解构赋值

对象的解构允许我们使用变量的名字匹配对象的属性,匹配成功将对象属性的值赋值给变量。

举例:

let person = {
     name:'张三',age:30,sex:'男'};
let {
     name,age,sex} = person;
console.log(name);
console.log(age);
console.log(sex);

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

let {
      bar, foo } = {
      foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"

let {
      baz } = {
      foo: 'aaa', bar: 'bbb' };
baz // undefined

对象的解构赋值特点

注意
如果解构失败,那么变量的值就是undefuned

let {
     foo} = {
     bar:'baz'};
console.log(foo); // undefined

默认值
对象的解构也可以指定默认值。和 数组默认值解构同样,对象的属性值也要严格等于undefined

var {
     x = 3} = {
     };
console.log(x);	// 3

var {
     x, y = 5} = {
     x: 1};
console.log(x);	// 1

var {
     x = 3} = {
     x: undefined};
console.log(x);	// 3

var {
     x = 3} = {
     x: null};
console.log(x);	// null

注意点
(1)如果要将一个已经声明的变量用于解构赋值,必须非常小心。

// 错误的写法
let x;
{
     x} = {
     x: 1};
// SyntaxError: syntax error

因为JavaScript引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。

// 正确的写法
let x;
({
     x} = {
     x: 1});

注意点
(2)由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

let arr = [1, 2, 3];
let {
     0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3

上面代码对数组进行对象解构。数组arr0键对应的值是1[arr.length - 1]就是2键,对应的值是3


字符串解构

字符串也可以解构,这是因为,此时字符串被转换成了一个类似数组的对象。举例如下:

const [a, b, c, d] = 'smyhvae';
console.log(a);
console.log(b);
console.log(c);
console.log(d);

console.log(typeof a);  //输出结果:string

数组的扩展


扩展运算符(…)

使用扩展运算符 就是将一个数组转为用逗号分隔的参数序列。

let arr = ["a","b","c"];
console.log(...arr); // 相当于 => console.log("a","b","c");

扩展运算符的应用

合并数组
扩展运算符提供了数组合并的新写法。

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

转换数组
利用扩展运算符将伪数组转换为真数组

var divs = document.querySelectorAll('div');
var arr = [...divs];
console.log(arr);

对象的扩展


对象的简单赋值

对象属性的简单赋值方式 可以将变量名直接放进对象中, 解析的时候 将变量名解析为属性名 变量值解析为属性值

1.属性的简单赋值

function fn(num1,num2) {
     
    return {
     num1,num2};
}

// 等同于

function fn(x,y) {
     
    return {
     num1:x,num2:y};
}
fn(1,2);

除了属性的简写,对象方法的也可以简单赋值 可以省略function关键字

var obj = {
     
    name :'lucy',
    age :18,
    study() {
     
     console.log("学习");
    }
}
// 等同于
var obj = {
     
    name :'lucy',
    age :18,
    study: function(){
     
     console.log("学习");
    }
}

super关键字

我们知道,this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向 当前对象的原型对象

const proto = {
     
  foo: 'hello'
};

const obj = {
     
  foo: 'world',
  find() {
     
    return super.foo;
  }
};

Object.setPrototypeOf(obj, proto);
obj.find() // "hello"

使用注意点 :

  1. 只能在对象的方法中使用 在其他地方使用都会报错
  2. 只能在对象方法的简写方式中使用
var stu = {
     
    name : "张三",
    age : 16,
    
    // study : function() {
     
    //    console.log("我是" +super.type + ", 我的任务是" + super.tesk); // 报错
    // }
    
    // 正确用法 : 
    study() {
     
        console.log("我是" +super.type + ", 我的任务是" + super.tesk);
    }
}

stu.__proto__ = {
     
    type : "student",
    tecsk : "学习"
}
  1. 对象的计算属性名 使用方式[] 对属性名进行计算, 里面可以是变量名 表达式 也可以是函数

对象的新增的方法


Object.is()

Object.is()
ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
Object.is() 用来比较两个值否严格相等,只是在-0 和+0 还有NaN的判断上面不一样

console.log(-0 === +0);  // true
console.log(NaN === NaN); // false

console.log(Object.is(-0, +0));  // false
console.log(Object.is(NaN, NaN));  // true

Object.assign()

Object.assign()
Object.assign()方法用于对象的合并
参数1 : 目标对象 参数2 : 需要被合并的对象

var obj1 = {
     
    name: "hello",
    age: 18
};
var obj2 = {
     
    sex: "男"
}
var newObj = Object.assign(obj1, obj2);
console.log(newObj);
//{name: "hello", age: 18, sex: "男"}

注意点
1.返回值是传入的第一个目标对象,会把所有的对象合并上去,再返回

var xm = {
     "name" : "张学友"};
var age = {
     "age" : 58};
var gender = {
     "gender" : "男"};

var objAssign = Object.assign(age,xm,gender);
console.log(objAssign);			//{age: 58, name: "张学友", gender: "男"}
console.log(objAssign === age);    // true 说明返回值是传入的第一个参数
console.log(objAssign === xm);	   // false
console.log(objAssign === gender); //false

注意点
2.第一个参数必须是对象 如果不是对象,就会把它转换成对象

var xm = {
     "name" : "张学友"};
var age = {
     "age" : 58};
var gender = {
     "gender" : "男"};
var objAssign = Object.assign(18, xm, gender);
console.log(objAssign);	
// Number {18, name: "张学友", gender: "男"}

注意点
3.如果第一个参数是undefined 或者null 因为他们无法转换成对象 那么就会报错

var xm = {
     "name" : "张学友"};
var age = {
     "age" : 58};
var gender = {
     "gender" : "男"};
var objAssign = Object.assign(undefined, xm, gender);
console.log(objAssign);	// 报错 Cannot convert undefined or null to object
var xm = {
     "name" : "张学友"};
var age = {
     "age" : 58};
var gender = {
     "gender" : "男"};
var objAssign = Object.assign(null, xm, gender);
console.log(objAssign);	// 报错 Cannot convert undefined or null to object

注意点
4.如果在需要合并的多个对象里面 有同名的属性 那么后面的属性就会对前面的进行覆盖

var xm = {
     "name" : "张学友","gender" : "男"};
var age = {
     "age" : 58};
var gender = {
     "gender" : "女","name": "lili"};
var objAssign = Object.assign(null, xm, gender);
console.log(objAssign);	// {age: 58, name: "lili", gender: "女"}

注意点
5.如果undefined 和 null 不是第一个参数就不会报错 而是把第一个参数返回

var xm = {
     "name" : "张学友"};
var age = {
     "age" : 58};
var gender = {
     "gender" : "男"};

var objAssign = Object.assign(age, xm, gender, undefined, null);
console.log(objAssign); // {age: 58, name: "张学友", gender: "男"}

注意点
6.Object.assign() 可以浅拷贝


Object.setPrototypeOf()

Object.setPrototypeOf()
设置一个对象的原型对象
参数1 : 目标对象 参数2 : 新设置原型的对象

let proto = {
     
    sayHi() {
     
        console.log("hello");
    }
}
let obj = {
     
    n1: 15,
    n2: 18
}
Object.setPrototypeOf(obj, proto);
obj.sayHi();	// hello

Object.getPrototypeOf()

Object.getPrototypeOf()
获取一个对象的原型对象。参数 : 需要获取对象的原型的对象名

 let proto = {
     
     sayHi() {
     
         console.log("hello");
     }
 }
 let obj = {
     
     n1: 15,
     n2: 18
 }
 Object.setPrototypeOf(obj, proto);
obj.sayHi();
console.log(Object.getPrototypeOf(obj));	// {sayHi: ƒ}

Object.keys()

Object.keys()
Object.keys() 是将对象所有的属性名获取到 添加到数组 并返回 返回的是一个数组
参数 : 需要遍历的对象名

 var stu = {
     
     name: "张三",
     age: 18,
     work: "无业游民"
 }

 console.log(Object.keys(stu));
//  ["name", "age", "work"]

Object.values()

Object.values()
Object.values() 是将对象所有的属性值获取到 添加到数组 并返回 返回的是一个数组

var stu = {
     
    name: "张三",
    age: 18,
    work: "无业游民"
}

console.log(Object.keys(stu));
// ["name", "age", "work"]
console.log(Object.values(stu));
// ["张三", 18, "无业游民"]

class关键字的基本使用


1. 传统构造函数的缺点 :

  1. 构造函数和原型方法及属性分离 不便于维护 降低可读性
  2. 原型对象可以被遍历修改
  3. 默认情况下构造函数可以被当做普通的函数进行调用 功能性不明确
  4. 原型中的方法也可以作为构造函数来用

2. ES6中的类的本质

其实class就是一颗语法糖 他的本质还是函数 而且是ES5里面的函数封装而成的

class Cat{
     
    constructor(name, type) {
     
       this.name = name; // this指向于类创建的实例化对象
        this.type = type;
    }
    
    work() {
     
        concole.log("抓老鼠");
    }
}

console.log(typeof  Cat);  // function

在类里面定义方法 可以省略function关键字

每创建一个类 都会默认有一个构造方法constructor() 相当于构造函数


3. 类的特点 :

  1. 类是不可枚举的
  2. 类的用法跟let和const一样和 具有暂时性死区 必须先声明 再使用
  3. 类里面的方法 不可以作为构造函数来使用
  4. 类在使用的时候 必须配合new关键字进行使用 直接调用会报错

字符串的扩展


模板字符串


概述

传统的 JavaScript 语言,输出模板通常是这样写的:

var stu = {
     
    name: "lisa",
    age: 12,
    place: "湖北武汉",
    grade: 6
}
document.querySelector("p").innerHTML = "我叫" + stu.name + ",我几年" + stu.age + ",我住在" + stu.place + ",今年上" + stu.grade;
  • 这种写法破拼接太繁琐 而且需要多次分割 不便于维护
  • 所有拼接的字符串只能一行 显示太长 不便阅读
  • 为了解决这种问题,ES6 引入了模板字符串

模板字符串的使用

  • 声明
let str = `我是一个模板字符串哦!!`;

特点:

内容可以直接出现换行符

// 之前我们写的字符串想要换行,必须通过 + 和''连接
let str = '
    '+ '
  • 沈腾
  • '
    + '
  • 艾伦
  • '
    '
'
; // 现在使用 `` 的方式 let str = `
  • 沈腾
  • 艾伦
`
;

变量拼接

// 之前的写法
let lovest = '沈腾';
let out = lovest + '是搞笑的演员';

// 现在写法
let lovest = '沈腾';
let out = `${
       lovest}是搞笑的演员`;
console.log(out); // 沈腾是搞笑的演员
  • 使用模板字符串 所有的内容写在反引号(``)里面 然后变量放在$() 里面
  var stu = {
     
      name: "lisa",
      age: 12,
      place: "湖北武汉",
      grade: 6
  }
  document.querySelector("p").innerHTML = `我叫${
       stu.name}, 我今年 ${
       stu.age}岁了, 我住在${
       stu.place}`;
  • 在 ${} 可以放入任意的js表达式 可以进行运算
 document.querySelector("p").innerHTML = `我叫${
       stu.name}, 我今年 ${
       stu.age - 1}岁了, 我住在${
       stu.place}`;

模板引擎

引入模板引擎的目的:

页面渲染使用字符串拼接 会存在问题: 字符串的恒定性 字符串拼接的时候容易出错

模板引擎不用拼接字符串 直接使用 静态页面里面的HTML里面的结构生成模板 需要渲染的时候直接调用

模板引擎的实现方式有很多 最简单的 ‘置换型’

模板引擎 这类模板引擎只是将指定模板内容( 字符串) 中的特定标记( 子字符串) 替换一下便生成了最终需要的业务数据( 网页)

模板引擎的使用步骤:

  • 导入模板引擎 template-web.js 下载地址-选择lib

  • 准备一个模板

 <script type="text/html" id="tpl">
     <h1>自我介绍</h1>
 	<p>大家好, 我叫
 	<%= name %>, 我今年
     <%= age %></p>
 </script>
 
  • 准备的模板必须使用script 模板引擎规定的只能使用script
  • 必须要给它一个id 在调用方法的时候使用
  • 必须要有type属性 而且type属性绝对不能使用text / javascript 否则就会解析成js代码
  • 调用方法 生成HTML结构

使用模板引擎注意点:

  • 挖坑的时候一定要注意 坑的名字一定要和对象的属性名一致
 <%= name %>
 // 那么下面的对象属性名是name才可以和模板字符串对应上
  • type的值只要不是text / javascript 但是建议使用text / html 因为其它的无法识别html标签

  • <%= %> 必须是一个整体 不能加空格 或者其它的符号

 <% = %>  // xxx 错误写法

模板引擎使用示例:

<script src="./template-web.js"></script>
<script type="text/html" id="tpl">
    <h1>自我介绍</h1>
	<p>大家好, 我叫
	<%= name %>, 我今年
    <%= age %></p>
</script>
<script>
        var stu = {
     
            name: "丽萨",
            age: 18
        }
var html = template("tpl", stu);
var stu2 = {
     
    xingming: "黑龙江",
    age: 16
}

html += template("tpl", stu2);
document.body.innerHTML = html;

var arr = [{
     
    name: "丽萨",
    age: 18
}, {
     
    name: "安迪",
    age: 18
}, {
     
    name: "艾玛",
    age: 18
}, {
     
    name: "路痴",
    age: 18
}]
</script>

显示效果:

接近两万字的ES6理解笔记_第1张图片


模板引擎的内部循环

 <script type="text/html" id="tpl">
        <%for(var i = 0; i < list.length;i++) {
      %>
            <h1>自我介绍</h1>
            <p>大家好, 我叫
                <%= list[i].name %>, 我今年
                    <% if(list[i].age >20) {
      %>
                        <u>成年</u>
                        <% } else {
      %>
                            <u>未成年</u>
                            <% } %>
            </p>
           <% } %>
</script>


模板引擎的案例

Demo

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>

    <style>
        input {
     
            width: 120px;
            height: 70px;
            margin: 50px auto;
            display: block;
        }
        
        table {
     
            margin: 0px auto;
            /* table的样式,其他地方不能用 */
            /* 合并单元格的边框 */
            border-collapse: collapse;
        }
        
        th,
        td {
     
            border: 1px solid #000;
            width: 120px;
            height: 40px;
            text-align: center;
        }
    </style>
</head>

<body>
    <!-- 1.导入模板引擎 -->
    <script src="./template-web.js"></script>

    <input type="button" value="加载数据" id="load">

    <table>
        <thead>
            <tr>
                <th>序号</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>成绩</th>
            </tr>
        </thead>

        <tbody id="tb">
            <!-- 2.准备模板 -->
            <script type="text/html" id="tpl">
                <!-- 遍历 -->
                <% for(var i = 0;i < list.length; i++) {
      %>
                    <tr>
                        <td>
                            <%= list[i].Id %>
                        </td>
                        <td>
                            <%= list[i].name %>
                        </td>
                        <td>
                            <%= list[i].age %>
                        </td>
                        <td>
                            <%= list[i].score %>
                        </td>
                    </tr>
                    <% }%>

            </script>
        </tbody>
    </table>
</body>

</html>


<script>
    var arr = [{
     
        Id: 0,
        name: "张三",
        age: 18,
        score: 68
    }, {
     
        Id: 1,
        name: "王亚",
        age: 28,
        score: 55
    }, {
     
        Id: 2,
        name: "李辉",
        age: 16,
        score: 70
    }, {
     
        Id: 3,
        name: "秦虹",
        age: 18,
        score: 50
    }, {
     
        Id: 4,
        name: "海庆",
        age: 19,
        score: 76
    }, {
     
        Id: 5,
        name: "蛋娃",
        age: 14,
        score: 27
    }, ];
    var html = "";
    html += template("tpl", {
     
        list: arr
    });
    document.getElementById("load").onclick = function() {
     
        document.getElementById("tb").innerHTML = html;
    }
</script>

上面的模板引擎全都是 <%= %> <% %> 不便于查看维护,下面改进一下,使用简洁一点的方法

// 模板引擎改进
<script type="text/html" id="tpl">
    <!-- 遍历 -->
    {
     {
      each list value }}
        <tr>
            <td> {
     {
      value.Id }} </td>
			<td> {
     {
      value.name }} </td>
              	 {
     {
      if(value.age >= 18) }}
              <td>成年</td>
				 {
     {
      else }}
               <td>未成年</td>
				 {
     {
      /if }}
				<td>
                  {
     {
      value.score }} 
                 </td>
            </tr>
       {
     {
      /each }}

</script>

标签模板

标签模板 他本质上不是模板 而是函数的另外一种调用形式 其实我们所说的标签 就是我们事先封装好的函数 他的参数就是后面拼接的字符串 以${} 作为分隔符 固定的字符串 放进数组里面 然后 ${} 里面的变量依次作为后面的参数

标记模板 (函数)的参数:

  • 参数1 : 被插入的变量分割成的字符串数组
  • 参数2… : 都是被插入的变量

示例:

var name = "jack";
        var boy = "男孩";
        var girl = "女朋友";
        var str = `我的名字叫${
       name},我是一个穷穷的${
       boy},我有一个很漂亮的${
       girl}`;
        console.log(str);


function intro(parts) {
     
            console.log(parts); 
    // ["我的名字叫",",我是一个",","穷穷的",",我有一个漂亮的","]
            console.log(arguments);
    
            // 伪数组转换真数组
            var res = Array.prototype.slice.call(arguments, 1);
            console.log(res);
            var s = "";
            for (let i = 0; i < res.length; i++) {
     
                s += `${
       parts[i]}${
       res[i]}`;
            }
            s += `${
       parts[parts.length-1]}`;
            return s;

        }
        var feature = "很绅士的";
        var hi = "大家好";
        var str = intro `${
       hi},我的名字叫${
       name},我是一个${
       feature}穷穷的${
       boy},我有一个很漂亮的${
       girl}`;
        console.log(str);


标签字符串的应用

Demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <textarea name="" cols="30" rows="10" id="txt"></textarea>
    <button id="btn">提交</button>
    <div id="container"></div>
    <script>
        var txt = document.getElementById("txt");
        var container = document.getElementById("container");
        document.getElementById("btn").onclick = function() {
     
            console.log(txt.value);
            container.innerHTML = protect `

${ txt.value}

${ txt.value}

`
; } function protect(parts) { console.log(parts); console.log(arguments); var res = Array.prototype.slice.call(arguments, 1); console.log(res); var str = ""; for (let i = 0; i < res.length; i++) { var s = res[i].replace(/, "<").replace(/>/g, ">"); console.log(s); str += parts[i] + s; if (i === res.length - 1) { str += parts[i + 1]; } } return str; } </script> </body> </html>

函数的扩展


函数参数的默认值

ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法

function fn(x,y) {
     
    y = y || "world";
    console.log(x,y);
}
fn("hello");	// hello world
fn("hello","");	// hello world

上面的代码会检查 函数 fny 有没有赋值,如果没有,就指定默认值为 world。这种写法缺点在于,如果参数 y 赋值了,但是赋的值是 false 那么该赋值就起不到作用,就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

所以ES6 允许为函数的参数设置默认值,即 直接写在参数定义的后面。

function add(a,b,c = 10) {
     
    return a + b + c;
}
let res = add(10,20);
console.log(res);	// 40

与解构赋值结合

// 不使用解构赋值的情况
function content(options) {
     
    let host = options.host;
    let username = options.username;
    let password = options.password;
    let port = options.port;
    console.log(host);
    console.log(username);
    console.log(password);
    console.log(port);
}

 // 使用解构赋值
function content({
      host = '127.0.0.1',username,password,port }) {
     
    console.log(host);
    console.log(username);
    console.log(password);
    console.log(port);
};

content({
     
    host: 'qq.com',
    username: 'root',
    password: 'root',
    port: 8080
});

我们可以看到不使用解构赋值的时候,每次都需要写 options. 但是使用了对象的解构赋值就简化了很多操作,而且还可以赋默认值。


箭头函数

ES6 允许使用“箭头”(=>)定义函数。

var fn = n => v;

// 等同于
var fn = function(n) {
     
    return v;
}

1.只有一个参数 并且函数体只有一句话 那么参数可以省略() 返回值可以省略 return 函数体省略了{}

 var fn = n => 123;
 var r = fn();
console.log(r);

2.如果函数体没有函数 或者有多个参数 那么参数中的()就不能省略

var fn = (x,y) => x + y;

3.如果箭头函数的函数体有多条代码 那么就不能省略 {} 以及返回值也不能省略return

 var study = () => {
     
     var lesson = "语文";
     return "好好学习,天天向上";
 }

4.如果函数体只有一句话 并且 返回值是对象 那么 这个返回值必须使用()包起来
函数体使用的是 {} 对象也是 {}, {}大括号会被解释为代码块

// 报错 
var info = (name, age) => {
     
     name: name,
     age: age
 };

// 不报错
var info = (name, age) => ({
     
     name: name,
     age: age
 });

5.如果对象只有一个键值对 那么不会报错 但是也没有正确的值
因为js引擎在解析的时候 {} 默认解析为函数体结构 函数体代码 name : name;

let foo = name=> {
      name:name };
foo("张三") // undefined

箭头函数的this

在ES6里面 箭头函数的this指向 是继承自父级执行上下文中的this

箭头函数没有自己的this, 他的this是从父级继承而来的 箭头函数的this在定义的时候就已经确定了

 var name = "我在window里面";
var obj = {
     
    name: "我是obj里面的",
    fn: function() {
     
        console.log(this);
        var that = this;
        setTimeout(function() {
     
            console.log(this.name); // "我在window里面"
            console.log(that.name); // "我是obj里面的"
        }, 1000)
    }
}
obj.fn();

setTimeout默认的this是指向 window 的,这里我们使用 that 保存了 了 obj 的this,所以this.naem是指向window的,而that.name 是obj。下面我们来看看箭头函数里面的this

var name = "我在window里面";
var obj = {
     
    name: "我是obj里面的",
    fn: function() {
     
        console.log(this);
        var that = this;
        setTimeout(() => {
     
            console.log(this.name); // "我是obj里面的" 
            console.log(that.name); // "我是obj里面的" 
        }, 1000)
    }
}
obj.fn();

that.name 是指向 obj 的,上边已经测试过没什么问题,那 this.name怎么也变成了 “我是obj里面的” 呢?再看以下代码:把 fn 也变成箭头函数

 var name = "我在window里面";
var obj = {
     
    name: "我是obj里面的",
    fn: () => {
     
        console.log(this);
        var that = this;
        setTimeout(() => {
     
            console.log(this.name); // "我在window里面"
            console.log(that.name); // "我在window里面" 
        }, 1000)
    }
}
obj.fn();

嗯?怎么都变成 “我在window里面” ? 想想开头的概述:“箭头函数没有自己的this, 他的this是从父级继承而来的” 。所以,我们得知,箭头函数的父级this指向哪里,那么箭头函数的this就指向哪里


箭头函数this注意点

1.不能作为构造函数实例化对象

let Person = (name,age) => {
     
    this.name = name;
    this.age = age;
}
let me = new Person('xiao',30);
console.log(me);
// 报错:Person is not a constructor

2.不能使用arguments 变量

let fn = () => {
     
    console.log(arguments);
}
fn(1,2,3,4);
// 报错:Identifier 'fn' has already been declared

箭头函数的应用场景

应用一:点击div 2s 后变成【pink 色】

 // 原来的做法
<div id="ad"></div>
<script>
    // 点击div 2s 后变成【pink 色】
    let ad = document.getElementById("ad");
ad.addEventListener("click", function() {
     
    // 保存this的值,用于定时器内(因为定时器的this指向window)
    let _this = this;
    setTimeout(function() {
     
        _this.style.backgroundColor = "pink";
    }, 2000);
});
</script>

现在可以直接使用箭头·函数实现

// 点击div 2s 后变成【pink 色】
let ad = document.getElementById("ad");
ad.addEventListener("click", function() {
     
    setTimeout( () => {
     
        this.style.backgroundColor = "pink";
    }, 2000);
});

应用二:从数组中返回偶数的元素

let arr =[1,6,9,10,500,25];
let res = arr.filter(function(item) {
     
    return item % 2 === 0;
});
console.log(res);	// 6 10 500

// 使用箭头函数
let arr =[1,6,9,10,500,25];
let res = arr.filter((item) => item % 2 === 0 );
console.log(res);	// 6 10 500
  • 箭头函数适合与this无关的回调:定时器,数组的方法回调
  • 箭头函数不适合与this有关的回调:DOM的事件回调,对象的方法
  • 不适合不代表不能用,某些场景下还是需要的。

this指向 扩展

1.普通函数的this : this就是谁调用 this就指向谁 this是在调用的时候确定的

function f1() {
     
    console.log(this);
}
f1();			//window
window.f1();	//window

2.对象里面的方法 它里面的this指向当前这个对象

var obj = {
     
    a: "111",
    b: "222",
    print: function() {
     
        console.log(this);
    }
}
obj.print();

var fn = obj.print;
var fn = obj.print();

fn = function() {
     
    console.log(this);
}
window.fn(); // this指向window

obj.f2 = fn;
obj.f2(); // this指向obj

3.定时器里面的this, 如果没有特殊的指向 那么 setInterval和setTimeout里面的回调函数的this一定是指向window


Symbol(待更新!!!)

Symbol 是ES6新引入的一种基本数据类型 表示的是 独一无二的值 他是js里面 第六种基本数据类型

  • 基本数据类型(ES5):string number boolean undefined null
  • typeof返回值(ES5):string number boolean undefined object function

你可能感兴趣的:(JS,笔记,es6)