ECMAScript6笔记

ECMAScript 6 笔记

ECMAScript 6 笔记

1.var、let 及 const

  • 全局申明的var变量会挂载在window上,而let和const不会

  • var声明变量存在变量提升,let和const不会

    • 变量提升就是就是JavaScript编译时会将当前作用域的所有变量的声明提升到程序的顶部(就是先把要用的定义了)
  • let、const 的作用范围是块级作用域,而var的作用范围是函数作用域

    • 全局作用域:window环境下执行的代码
    • 函数作用域:由于函数执行,会产生作用域,这块作用域内存放着当前函数内部执行的代码中的变量,函数。
    • 块级作用域:声明的变量只会在当前作用域及其以下的作用域可以访问的到,一般就是{}n内
  • 同一作用域下let和const不能声明同名变量,而var可以

  • 同一作用域下在let和const声明前使用会存在暂时性死区

    • 暂时性死区:使用let命令声明变量之前,该变量都是不可用的(ReferenceError)
  • const

    • 一旦声明必须赋值,不能使用null占位
    • 声明后不能再修改
    • 如果声明的是复合类型数据(比如对象),可以修改其属性
  • 例子:

<!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>
    <script>
        window.onload= function () {
            /*
            var aBtn = document.getElementsByTagName('input')
            for (var i=0; i < aBtn.length; i++) {
                aBtn[i].onclick = function () {
                    alert(i) //3,3,3
                }
            }*/
            var aBtn = document.getElementsByTagName('input')
            for (let i = 0; i < aBtn.length; i++) {
                aBtn[i].onclick = function () {
                    alert(i) //1,2,3
                }
            }
            /*
            var aBtn = document.getElementsByTagName('input')
            for (var i = 0; i < aBtn.length; i++) {
                // 封装到函数里,限制作用域
                (function (i) {
                    aBtn[i].onclick = function () {
                        alert(i)
                    }
                })(i)
            }*/
        }
    </script>
</head>
<body>
    <input type="button" value="按钮1">
    <input type="button" value="按钮2">
    <input type="button" value="按钮3">
</body>
</html>

2.箭头函数

  • 箭头函数,就是简写函数
    • 如果只有一个参数,() 可以省
    • 如果只有一个return{}可以省
// 普通函数
function name() {

}
// 箭头函数,去掉 function, 加上 =>
() => {

}
let show1 = function () {
    console.log('abc')
}

let show2 = () => {
    console.log('abc')
}

show1() // 调用函数
show2()

let show4 = function (a) {
    return a*2
}

let show5 = a => a * 2  //简洁,类似python lambda 函数

console.log(show4(10))
console.log(show5(10))
Es6中箭头函数与普通函数的区别?
  • 普通function的声明在变量提升中是最高的,箭头函数没有函数提升
  • 箭头函数没有属于自己的thisarguments
  • 箭头函数不能作为构造函数,不能被new,没有property
  • 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数
  • 不可以使用 new 命令,因为:
    • 没有自己的 this,无法调用 call,apply
    • 没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 __proto__

3.函数参数

  • 参数扩展/展开 ...args

    • 收集剩余的参数,只能作为最后一个参数
    • 展开数组的简写
  • 默认参数

function show(a, b, ...args) {
    console.log(a)
    console.log(b)
    console.log(args)
}
console.log(show(1, 2, 3, 4, 5))

let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
let arr3 = [...arr1, ...arr2]
console.log(arr3)

function show2(a, b=5, c=8) {
    console.log(a, b, c)
}
show2(88, 12)

4.解构赋值

let [a, b, c] = [1, 2, 3]
console.log(a, b, c)

let {x, y, z} = {x: 1, y: 2, z: 3}
console.log(x, y, z)

let [json, arr, num, str] = [{ a: 1, b: 2 }, [1, 2, 3], 8, 'str']
console.log(json, arr, num, str)
  • 解构赋值
    • 赋值语句左右两边结构必须相同
    • 声明和赋值赋值需由一条语句完成

5.数组

  • 增加map(), filter(), reduce(), foreach() 四个方法

  • map() 映射
    ######map()作用是遍历原数组,将每个元素拿出来做一些变换然后返回一个新数组,原数组不发生改变

let arr = [12, 5, 8]
let result = arr.map(function (item) {
    return item * 2
})
let result2 = arr.map(item=>item * 2)
console.log(result)
console.log(result2)

let score = [18, 86, 88, 24]
let result3 = score.map(item => item >= 60 ? '及格' : '不及格')
console.log(result3)

[ 24, 10, 16 ]
[ 24, 10, 16 ]
[ '不及格', '及格', '及格', '不及格' ]
['1','2','3'].map(parseInt)
// -> [ 1, NaN, NaN ]
//['1','2','3'].map(parseInt)
  // 接收两个参数parseInt(string,radix) radix区间范围介于2~36之间 没有或为0时按照10来执行
  parseInt('1',0);// radix 为 0,parseInt() 会根据十进制来解析,所以结果为 1
  parseInt('2',1);// radix 为 1,超出区间范围,所以结果为 NaN
  parseInt('3',2);// radix 为 2,用2进制来解析,应以 0 和 1 开头,所以结果为 NaN
  • reduce() 汇总
    ######可以将数组中的元素通过回调函数最终转换为一个值
    • 用于计算总和,平均数等
var arr = [1, 3, 5, 7]
var result = arr.reduce(function (tmp, item, index) {
    // tmp 上次结果,item当前数,index次数1开始
    console.log(tmp, item, index)
    return tmp + item
})
console.log(result)
const arr = [1, 2, 3]
const sum = arr.reduce((acc, current) => acc + current, 0)
console.log(sum)
var arr = [1, 3, 5, 7]
var result = arr.reduce(function (tmp, item, index) {
    if (index != arr.length - 1) { // 不是最后一次
        return tmp + item
    } else {
        return (tmp + item)/arr.length
    }
})
console.log(result) // 平均值
  • filter() 过滤器
    ######filter 的作用是生成一个新数组,在遍历数组的时候将返回值为 true 的元素放入新数组,可以利用这个函数删除一些不需要的元素
var arr = [12, 4, 8, 9]
var result = arr.filter(item => (item % 3 === 0) ? true : false)
console.log(result)
var result = arr.filter(item => item % 3 === 0)
console.log(result)

var arr = [
    { title: '苹果', price: 10 },
    { title: '西瓜', price: 20 },
]
var result = arr.filter(json => json.price >= 20)
console.log(result)
  • forEach 循环迭代
var arr = [12, 4, 8, 9]
var result = arr.forEach(item => {console.log(item)})
var result = arr.forEach((item, index) => {console.log(item, index)})

6.字符串

  • 两个新方法
    • startsWith
    • endsWith
var url = 'http://qq.com'
console.log(url.startsWith('http'))// true
console.log(url.endsWith('com'))// true

  • 字符串模版
    • 使用反引号,${变量}
    • 可以折行
let a = 12
let str1 = `asdf${a}`
console.log(str1)
let title = '标题'
let content = '内容'
let str = `<div>
<h1>${title}</h1>
<p>${content}</p>
`
console.log(str)
<div>
<h1>标题</h1>
<p>内容</p>

7.面向对象

  • 原先写法
    • 类和构造函数一样
    • 属性和方法分开写
// 原先
function User(name, pass) {
    this.name = name
    this.pass = pass
}

User.prototype.showName = function () {
    console.log(this.name)
}
User.prototype.showPass = function () {
    console.log(this.pass)
}

var u1 = new User('able', '1233')
u1.showName()
u1.showPass()

// 原先的继承
function VipUser(name, pass, level) {
    User.call(this, name, pass)
    this.level = level
}
VipUser.prototype = new User()
VipUser.prototype.constructor = VipUser
VipUser.prototype.showLevel = function () {
    console.log(this.level)
}

var v1 = new VipUser('blue', '1234', 3)
v1.showName()
v1.showLevel()

  • 新版面向对象(类Java)
    • 有了 class 关键字、构造器
    • class 里面直接加方法
    • 继承,super 超类==父类
class User {
    constructor(name, pass) {
        this.name = name
        this.pass = pass
    }

    showName() {
        console.log(this.name)
    }
    showPass() {
        console.log(this.pass)
    }
}

var u1 = new User('able2', '111')
u1.showName()
u1.showPass()

// 新版本继承
class VipUser extends User {
    constructor(name, pass, level) {
        super(name, pass)
        this.level = level
    }
    showLevel(){
        console.log(this.level)
    }
}

v1 = new VipUser('blue', '123', 3)
v1.showLevel()

8.json

  • JSON 格式

    • 只能用双引号
  • JSON 对象

    • JSON 对象是 JavaScript 的原生对象,用来处理 JSON 格式数据,有两个静态方法
    • JSON.parse(string) :接受一个 JSON 字符串并将其转换成一个 JavaScript 对象
    • JSON.stringify(obj) :接受一个 JavaScript 对象并将其转换为一个 JSON 字符串
var json = {a: 12, b: 5}
var str = 'hi,' + JSON.stringify(json)
var url = 'http://www.xx.com/' + encodeURIComponent(JSON.stringify(json))
console.log(str)
console.log(url)

var str = '{"a": 12, "b": 4, "c": "abc"}'
var json = JSON.parse(str)
console.log(json)

// hi,{"a":12,"b":5}
// http://www.xx.com/%7B%22a%22%3A12%2C%22b%22%3A5%7D
// { a: 12, b: 4, c: 'abc' }

9.Promise

  • Promise 对象

    • 用同步的方式来书写异步代码
    • Promise 让异步操作写起来,像在写同步操作的流程,不必一层层地嵌套回调函数
    • 改善了可读性,对于多层嵌套的回调函数很方便
    • 充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口
  • Promise 的三种状态

    • 等待中(pending)
    • 完成了(resolved)
    • 拒绝了(rejected)
  • Promise 也是一个构造函数

    • 接受一个回调函数f1作为参数,f1里面是异步操作的代码
    • 返回的p1就是一个 Promise 实例
    • 所有异步任务都返回一个 Promise 实例
    • Promise 实例有一个then方法,用来指定下一步的回调函数
function f1(resolve, reject) {
  // 异步代码...
}
var p1 = new Promise(f1);
p1.then(f2); // f1的异步操作执行完成,就会执行f2。

当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的。

new Promise((resolve, reject) => {
  console.log('new Promise')
  resolve('success')
})
console.log('finifsh')

// 先打印new Promise, 再打印 finifsh
  • Promise 使得异步流程可以写成同步流程
// 传统写法(会造成回调地狱)
step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        // ...
      });
    });
  });
});

// Promise 的写法
(new Promise(step1))
  .then(step2)
  .then(step3)
  .then(step4);
  • Promise.all(promiseArray)方法
    • 将多个Promise对象实例包装,生成并返回一个新的Promise实例
    • promise数组中所有的promise实例都变为resolve的时候,该方法才会返回
    • 并将所有结果传递results数组中
    • promise数组中任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象
var p1 = Promise.resolve(1),
    p2 = Promise.resolve(2),
    p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {
    console.log(results);  // [1, 2, 3]
});
  • Promise.race([p1, p2, p3])

    • Promise.race就是赛跑的意思,哪个结果获得的快,就返回那个结果
    • 不管结果本身是成功状态还是失败状态
  • 另外,Promise也存在一些缺点,比如无法取消 Promise,错误需要通过回调函数捕获

10.generator - yield

function *foo(x) {
  let y = 2 * (yield (x + 1))
  let z = yield (y / 3)
  return (x + y + z)
}
let it = foo(5)
console.log(it.next())   // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}
  • 首先 generator 函数调用和普通函数不同,它会返回一个迭代器

  • 当执行第一次 next 时,传参会被忽略,并且函数暂停在 yield (x + 1) 处,所以返回 5 + 1 = 6

  • 当执行第二次 next 时,传入的参数等于上一个 yield 的返回值,如果你不传参,yield 永远返回 undefined。此时 let y = 2 * 12,所以第二个 yield 等于 2 * 12 / 3 = 8

  • 当执行第三次 next 时,传入的参数会传递给 z,所以 z = 13, x = 5, y = 24,相加等于 42

11. async 和 await

一个函数如果加上 async ,那么该函数就会返回一个 Promise

async function test() {
  return "1"
}
console.log(test()) 
// -> Promise {<resolved>: "1"}

async 就是将函数返回值使用 Promise.resolve() 包裹了一下,和 then 中处理返回值一样,并且 await 只能配套 async 使用。

async function test() {
  let value = await sleep()
}

当然也存在一些缺点,因为 await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低。

async function test() {
  // 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
  // 如果有依赖性的话,其实就是解决回调地狱的例子了
  await fetch(url)
  await fetch(url1)
  await fetch(url2)
}

一个使用 await 的例子:

let a = 0
let b = async () => {
  a = a + await 10
  console.log('2', a)
}
b()
a++
console.log('1', a)

//先输出  ‘1’, 1
//在输出  ‘2’, 10
  • 首先函数 b 先执行,在执行到 await 10 之前变量 a 还是 0,因为 await 内部实现了 generator ,generator 会保留堆栈中东西,所以这时候 a = 0 被保存了下来
  • 因为 await 是异步操作,后来的表达式不返回 Promise 的话,就会包装成 Promise.reslove(返回值),然后会去执行函数外的同步代码
  • 同步代码 a++ 与打印 a 执行完毕后开始执行异步代码,将保存下来的值拿出来使用,这时候 a = 0 + 10

上述解释中提到了 await 内部实现了 generator,其实 await 就是 generator 加上 Promise 的语法糖,且内部实现了自动执行 generator

12. Proxy

Proxy 可以用来自定义对象中的操作。 Vue3.0 中将会通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。

let p = new Proxy(target, handler)

target 代表需要添加代理的对象,handler 用来自定义对象中的操作,比如可以用来自定义 set 或者 get 函数。

let onWatch = (obj, setBind, getLogger) => {
  let handler = {
    set(target, property, value, receiver) {
      setBind(value, property)
      return Reflect.set(target, property, value)
    },
    get(target, property, receiver) {
      getLogger(target, property)
      return Reflect.get(target, property, receiver)
    }
  }
  return new Proxy(obj, handler)
}

let obj = { a: 1 }
let p = onWatch(
  obj,
  (v, property) => {
    console.log(`监听到属性${property}改变为${v}`)
  },
  (target, property) => {
    console.log(`'${property}' = ${target[property]}`)
  }
)
p.a = 2 // 控制台输出:监听到属性a改变
p.a // 'a' = 2

自定义 set 和 get 函数的方式,在原本的逻辑中插入了我们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。

当然这是简单版的响应式实现,如果需要实现一个 Vue 中的响应式,需要我们在 get 中收集依赖,在 set 派发更新,之所以 Vue3.0 要使用 Proxy 替换原本的 API 原因在于 Proxy 无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是 Proxy 可以完美监听到任何方式的数据改变,唯一缺陷可能就是浏览器的兼容性不好了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值