es5/es6中新增的方法

ES5中的新增方法

ES5 中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:

  • 数组方法
  • 字符串方法
  • 对象方法

1.1 数组方法

迭代(遍历)方法:forEach()、map()、filter()、some()、every();

array.forEach(function(currentValue, index, arr))
    • 让数组中每个元素都执行一次 fn,相当于 for 循环
    • 不会占用全局变量,结合箭头函数贼舒服
    • currentValue:当前项的值
    • index:当前项的索引
    • arr:当前数组对象本身
array.map(function(currentValue, index, arr))
    • map() 方法会得到一个新数组, 新数组中包含每次函数调用返回的结果
    • 它返回的是一个新数组
array.filter(function(currentValue, index, arr))
    • filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
    • 注意它直接返回一个新数组
    • currentValue: 当前项的值
    • index:当前项的索引
    • arr:当前数组对象本身
array.some(function(currentValue, index, arr))
    • some() 方法用于检测数组中的元素是否满足指定条件,即查找数组中是否有满足条件的元素。
    • 它的返回值是布尔值,如果查找到这个元素,就返回true , 如果查找不到就返回false。
    • 如果找到第一个满足条件的元素,则终止循环,不再继续查找。
    • currentValue: 当前项的值
    • index:当前项的索引
    • arr:当前数组对象本身
array.every(function(currentValue, index, arr))
    • every() 方法检测数组中的所有元素是否满足指定条件。
    • 所有的元素都返回true,结果才是true

1.2 字符串方法

trim() 方法会从一个字符串的两端删除空白字符。

str.trim()
  • trim() 方法并不影响原字符串本身,它返回的是一个新的字符串

1.3 对象方法

  1. Object.keys() 用于获取对象自身所有的属性

    Object.keys(obj)
    • 效果类似 for…in

    • 返回一个由属性名组成的数组

  2. Object.defineProperty() 定义对象中新属性或修改原有的属性。

    Object.defineProperty(obj, prop, descriptor)
    • obj:必需。目标对象

    • prop:必需。需定义或修改的属性的名字

    • descriptor:必需。目标属性所拥有的特性,以对象形式 { } 书写

      • value: 设置属性的值 默认为undefined

      • writable: 值是否可以重写。true | false 默认为false

      • enumerable: 目标属性是否可以被枚举。true | false 默认为 false

      • configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false 默认为false

 

ES6的新增语法

1. let

  ES6中新增的用于声明变量的关键字。

  • let声明的变量只在所处于的块级有效。

     if (true) { 
         let a = 10;
     }
     console.log(a) // a is not defined

    注意:使用 var 声明的变量不具备块级作用域特性,使用 let 关键字声明的变量具有块级作用域,可以防止循环变量变成全局变量。

  • 不存在变量提升

    console.log(a); // a is not defined 
    let a = 20;
  • 暂时性死区

    var num = 10;
    if(true) {
        console.log(num); // num is not defined
        let num = 20;
    }
    // 在if的{}中不会向上一级查找num,因为在这个块级作用域中用了let关键字声明了num,变量num就和{}这个块级进行了整体绑定,所以在声明之前使用它会报错
  • 经典面试题

    // 题1
    var arr = [];
    for(var i = 0; i < 2; i++) {
        arr[i] = function() {
            console.log(i);
        }
    }
    arr[0](); // 2
    arr[1](); // 2
    // 解析:变量 i 是全局的,函数执行时输出的都是全局作用域下的 i 值。
    
    
    // 题2
    let arr = [];
    for(let i = 0; i < 2; i++) {
        arr[i] = function() {
            console.log(i);
        }
    }
    arr[0](); // 0
    arr[1](); // 1
    // 解析:每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的值。

2. const

const 用于声明常量,常量就是值(内存地址)不能变化的量。

  • 具有块级作用域

     if (true) { 
         const a = 10;
     }
     console.log(a) // a is not defined
  • 声明常量时必须赋值

    const PI; // Missing initializer in const declaration
  • 常量赋值后,值不能修改。

     const PI = 3.14;
     PI = 100; // Assignment to constant variable. 
     const ary = [100, 200];
     ary[0] = 'a';
     ary[1] = 'b';
     console.log(ary); // ['a', 'b']; 
     ary = ['a', 'b']; // Assignment to constant variable.

let、const、var 的区别

  1. 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象

  2. 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升

  3. 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值

varletconst
函数级作用域 块级作用域 块级作用域
变量提升 不存在变量提升 不存在变量提升
值可更改 值可更改 值不可更改

3. 解构赋值

ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构。

按照一定模式,从数组中或对象中提取值,将提取出来的值赋值给另外的变量。

3.1 数组解构

let [a, b, c] = ['pink', 'yellow', 'blue']; //正常解构赋值
console.log(a, b, c);

如果解构不成功,变量的值为undefined。

let [foo] = [];
let [bar, foo] = [1];
let [, c] = ['red', 'yellow'];  //选择性解构赋值

3.2 对象解构

let {name, age} = {name: 'andy', age: 18};  // 正常解构赋值,使用变量的名字匹配对象的属性,匹配成功,将对象属性的值赋值给变量。
console.log(name, age); // andy 18
​
let {name: myName, age: myAge} = {name: 'andy', age: 18}; //myName myAge 属于别名
​
let {name = 'lily', age } = {name: 'andy', age: 18};  //给name设置了默认值'lily'

4. 箭头函数

ES6中新增的定义函数的方式。

// 语法:() => {}
const fn = () => {}
fn();
  • 函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号

function sum(a, b) {
    return a + b;
}
const sum = (a, b) => a + b;
sum(10, 20);
  • 如果形参只有一个,可以省略小括号

function fn (v) {
    return v;
} 
const fn = v => v;
  • 箭头函数不绑定 this 关键字,它的 this 值是继承它的父作用域的,所以箭头函数不能作为构造函数。

    箭头函数的 this 值是词法作用域,也就是说它在定义的时候就被指定了的,而且也不会随着它调用方法的改变而改变。

    所以箭头函数中的this,指向的是函数定义位置的上下文this

const obj = {name: 'andy'};
function fn() {
    console.log(this); // obj
    return () => {
        console.log(this); // obj
    }
}
const resFn = fn.call(obj);
resFn();

注:在对象的方法中也不要用箭头函数(如果用箭头函数,就没法用this),注册事件的函数也不要用箭头函数。

  • 面试题解析:
var obj = {
    age: 20,
    say: () => {
        console.log(this.age)
    }
}
obj.say(); // undefined
// 解析:箭头函数中没有自己的this,它的this指向箭头函数定义区域的this。此例中的箭头函数定义在了obj这个对象中,它是一个对象,不能产生作用域,所以这个箭头函数被定义在了全局作用域下,所以在调用这个方法的时候this指向window

5. 剩余参数

剩余参数语法允许我们将一个不定数量的参数表示为一个数组。

const sum = (...args) => {
    let total = 0;
    args.forEach(item => total += item);
    return total;
}
console.log(sum(10, 20)); // 30
console.log(sum(1, 2, 3, 4)); // 10

剩余参数和解构配合使用

let students = ['andy', 'jack', 'lily'];
let [s1, ...s2] = students;
console.log(s1, s2); // 'andy', ['jack', 'lily']

6. ES6的内置对象扩展

6.1 扩展运算符(展开语法)

  • 扩展运算符可以将数组或者对象转为用逗号分隔的参数序列。

let ary = [1, 2, 3];
...ary  // 1, 2, 3
console.log(...ary);    // 1 2 3
  • 扩展运算符可以应用于合并数组

// 方法一 
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];
// 方法二 
ary1.push(...ary2);
  • 将类数组或可遍历对象转换为真正的数组

let oDivs = document.getElementsByTagName('div'); 
oDivs = [...oDivs];

6.2 构造函数方法:Array.from()

  • 将类数组或可遍历对象转换为真正的数组

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}; 
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
  • 方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

let arrayLike = { 
    "0": 1,
    "1": 2,
    "length": 2
}
let newAry = Array.from(aryLike, item => item *2)

6.3 实例方法:find()

用于找出第一个符合条件的数组成员,如果没有找到返回undefined

let ary = [
    { id: 1, name: 'andy' },
    { id: 2, name: 'lily' }
]
let target = ary.find(item => item.id === 2);

6.4 实例方法:findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1

let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9); 
console.log(index); // 2

6.5 实例方法:includes()

判断字符串是否包含另一个字符串,传统的 js 方法: indexOf 返回值是否等于 -1 ,如果等于 -1说明不包含

es6 中提供了方法 includes() 返回布尔值,表示是否包含给定的值。

[1, 2, 3].includes(2) // true 
1, 2, 3].includes(4) // false
​
let s = 'hello world'
s.includes('hello', 6) // false  第一个参数是需要匹配的数据,第二个参数是从第几个开始

7. 模板字符串

ES6新增的创建字符串的方式,使用反引号定义 ``。

  • 模板字符串中可以解析变量。

let name = '张三'; 
let sayHello = `hello,my name is ${name}`; // hello, my name is zhangsan
  • 模板字符串中可以保留空格和换行

let result = { 
    name: 'zhangsan', 
    age: 20,
    sex: '男' 
} 
let html = ` <div>
    <span>${result.name}</span>
    <span>${result.age}</span>
    <span>${result.sex}</span>
</div> `;
  • 在模板字符串中可以调用函数。

const sayHello = function () { 
   return '哈哈哈哈 追不到我吧 我就是这么强大';
}; 
let greet = `${sayHello()} 哈哈哈哈`;
console.log(greet); // 哈哈哈哈 追不到我吧 我就是这么强大 哈哈哈哈

8. String 的扩展方法

8.1 实例方法:startsWith()和endsWith()

  • startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
  • endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值
let str = 'Hello world!';
str.startsWith('Hello') // true 
str.endsWith('!')       // true

8.2 实例方法:repeat()

repeat() 方法表示将原字符串重复n次,返回一个新字符串。

'x'.repeat(3)      // "xxx" 
'hello'.repeat(2)  // "hellohello"

9.Set 数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

  • Set本身是一个构造函数,用来生成 Set 数据结构。

const s = new Set();
  • Set函数可以接受一个数组作为参数,用来初始化。

const set = new Set([1, 2, 3, 4, 4]);

例如:利用set去重,得到一个新数组

let set = new Set([1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1])
console.log(set)
console.log([...set])

注:set 去重之后得到的 set 类型的数据,所以还得把这个 set 数据类型转换成数组类型,这个时候又可以用到展开符这个好东西了

实例方法

    • add(value):添加某个值,返回 Set 结构本身

    • delete(value):删除某个值,返回一个布尔值,表示删除是否成功

    • has(value):返回一个布尔值,表示该值是否为 Set 的成员

    • clear():清除所有成员,没有返回值

const s = new Set();
s.add(1).add(2).add(3); // 向 set 结构中添加值 
s.delete(2)             // 删除 set 结构中的2值 
s.has(1)                // 表示 set 结构中是否有1这个值 返回布尔值 
s.clear()               // 清除 set 结构中的所有值

遍历

Set 结构的实例与数组一样,也拥有 forEach 方法,用于对每个成员执行某种操作,没有返回值。

s.forEach(value => console.log(value))

10. Promise

promise 用于更优雅的处理异步请求,其实就是对于回调函数的另一种写法,可以帮助我们避免回调地狱

回调地狱:比如第二个请求依赖第一个请求的结果进行请求,第三个请求依赖第二个,这样一层一层嵌套,。。。 为了解决回调顺序的不确定性以及地狱,就有了 promise 机制。

1、Promise 一般来讲是一个对象, .then方法其实就是 promise 对象的一个方法

2、Promise 构造函数接受一个函数作为参数,在这个函数中,可以进行异步的操作的代码

3、在这个函数中执行完成异步操作之前,并不是要调用用户提供的回调函数(不关心回调到底是什么),而是在这个函数中,异步完成之后,只需要去修改当前的promise 的状态

4、Promise的状态:

    • pendding :挂起,当前 promise 执行的任务,正在执行中
    • fullfilled: 完成,当前 promise 对象执行的任务,已经完成,并且是成功状态
    • rejected: 完成,当前 promise 对象执行的任务,已经完成,并且是失败的状态

5、Promise 对象的 .then 方法中接收到的成功的回调函数,会在当前的 promise 对象处于成功(fullfilled)状态的时候自动执行;

      Promise对象的 .then 方法中接收到的失败的回调函数,会在当前的 promise 对象处于失败(rejected)状态的时候自动执行

6、resolve 是一个函数,调用这个函数,就可以把当前 promise 对象标记成功

      reject 也是一个函数,调用这个函数,可以把当前 promise 对象标记失败

var p = new Promise(function(resolve, reject) {
    resolve() // 成功时调用
    reject() // 失败时调用
});
p.then(function(res) {
    // 从resolve得到正常结果
}, function(res) {
    // 从reject得到错误信息
})

resolve 和 reject在调用的时候,可以传递数据,这个数据会最终被传递到成功或者失败的回调函数中。

// 封装一个支持Promise APi的延时函数
function timeOut(time){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            // 这个回调函数中,不需要涉及任何具体的业务操作
            // resolve(123); // 可传递数据,这个数据会最终被传递到成功回调函数中
            reject();
        }, time);
    });
}

7、在 then 方法中,我们可以直接 return 数据而不是 Promise 对象,在后面的 then 中就可以接收到数据了。

then 参数中的函数返回值:

    1. 返回 Promise 实例对象

    • 返回的该实例对象会调用下一个 then

    2. 返回普通值

    • 返回的普通值会直接传递给下一个 then,通过 then 参数中函数的参数接受该值

timeOut(1000)
.then(num => {
    console.log(num + '这是1s后打印的内容')
    return timeOut(1000)
})
.then(num => {
    console.log(num + '这是2s后打印的内容')
    return timeOut(1000)
})
.then(num => {
    console.log(num + '这是3s后打印的内容')
    return timeOut(1000)
})

8、语法

语法一:

promise.then(
    function() {
        console.log('成功的回调')
    },
    function() {
        console.log('失败的回调')
    }
)

语法二:

Promise
.then(function() {
    console.log('成功的回调')
})
.catch(function() {
    consloe.log('失败的回调')
})

语法二和语法一的效果一样。不过它还有另外一个作用:在执行 resolve 的回调(也就是上面then中的参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死,而是会进到这个catch方法中。

8、promise 的其他用法

  • Promise.all: 只有当传入的所有 promise 的结果为 reslove,才会执行这个 all 的回调函数(即触发 then 方法执行里面的代码),否则只要有一个是 reject,就会执行 catch 方法。
Promise
    .all([t1, t2, t3, t4, t5])
    .then(function(){
        console.log("所有异步操作完成了");
    }
    .catch(function() {
        console.log("嗷嗷~~~有操作出错了");
    })
)
  • Promise.race:由第一个 promise 返回的状态决定,如果第一个 promise 返回的结果为 reslove 就会执行这个race的回调
Promise.race([t1, t2, t3, t4, t5]).then(function(){
    console.log("有一个异步率先完成了");
})

 

常用API:

    • .then() 得到异步任务的成功信息
    • .catch() 获取异常信息
    • .finally() 成功与否都会执行(还不是正式标准)
    • Promise.all() 并发处理多个异步任务,所有任务都执行完成才能得到结果
    • Promise.race() 只要有一个任务完成就能得到结果
Promise.all([p1, p2, p3]).then(res => {
    console.log(res); //三个结果的数组
})
Promise.race([p1, p2, p3]).then(res => {
    console.log(res);
})

 

示例:基于Promise处理Ajax请求:

function queryData(url) {
    return new Promise(function(resolve, reject){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
            if(xhr.readystate !== 4) return;
            if(xhr.readystate === 4 && xhr.status === 200){
                resolve(xhr.responseText);
            }else{
                reject('出错了');
            }
        }
        xhr.open('get', url);
        xhr.send(null);
    })
}
queryData('http://localhost:3000/data')
    .then(function(res){
        console.log(res);
    }, function(err){
        console.log(err)
    })
​
//发送多次请求
queryData('url1')
    .then(function(data){
        return queryData('url2');
    })
    .then(function(data){
        return queryData('url3');
    })
    .then(function(data){
        console.log(data);
    })

 

这里顺便叨叨一下 Promise 的语法糖:async/await 的基本用法

    • async 关键字用于函数上(async 函数的返回值是 Promise 实例对象)
    • await 关键字用于 async 函数中(await 可以得到异步的结果)
async function queryData(id){
    const ret = await axios.get('/data');
    return ret;
}
queryData.then(ret => {
    console.log(ret);
})

使用async后,代码变得更简洁,代码的逻辑和数据流都变得更加清晰。由于 async 的原理还是以 Promise 为基础,所以部分时候,async 是要和 Promise 配合使用的,它并不能完全脱离 Promise 独立运行;await 必须在 async 函数里使用,不能单独使用。

多个异步请求的场景

async function queryData(id){
    const info = await axios.get('/async1');
    const ret = await axios.get(`/async2?info=`+info.data);
    return ret;
}
queryData.then(ret => {
    console.log(ret);
})

11. 模块化

模块化就是把单独的一个功能封装到一个模块(文件)中,模块之间相互隔离,但是可以通过特定的接口公开内部成员,也可以依赖别的模块。

模块化开发的好处:方便代码的重用,从而提升开发效率,并且方便后期的维护。

ES6默认导出与默认导入

    • 默认到处语法 export default 默认导出的成员
    • 默认导入语法 import 接收名称 from ‘模块标识符’
//当前文件模块为 m1.js//定义私有成员a和c
let a = 10;
let c = 20;
let d = 30;
function show(){}
​
//将本模块中的私有成员暴露出去,供其他模块使用
export default {
    a,
    c,
    show
}
​
//导入模块成员
import m1 from './m1.js'
​
console.log(m1)
//打印输出的结果为{a: 10, b: 20, show: [function: show]}
//外界访问不到d,因为它没有被暴露出去

注意:每个模块中,只允许使用唯一的一次 export default,否则会报错

按需导出与按需导入

    • 默认导出语法 export let s1 = 10
    • 默认导入语法 import { s1 } from '模块标识符'
//当前文件模块为 m1.js
export let s1 = 'aaa'
export let s2 = 'bbb'
export function say = function(){}
​
//导入模块成员
import {s1, s2 as ss2, say} from './m1.js'
console.log(s1); // aaa
console.log(ss2); // bbb
console.log(say); // [function: say]

注意:每个模块中,可以使用多次按需导出

直接导入并执行模块代码

只执行模块中的代码,并不需要得到模块中向外暴露的成员,此时可以直接导入并执行模块代码。

//当前文件模块为 m2.js
//在当前模块中执行一个 for 循环操作
for(let i = 0; i < 3; i++) {
    console.log(i);
}
​
//直接导入并执行模块代码
import './m2.js'

 

posted @ 2020-05-29 16:36  sugar_coffee  阅读(657)  评论(0编辑  收藏  举报