ES6汇总

严格模式:use strict

<script>
	"use strict"    
</script>

声明区别

推荐使用const,定时器和循环要用let

  1. var 没有块级作用域,会变量提升,可以重复赋值。声明的变量拥有全局作用域或局部作用域
  2. let const 有块级作用域,没有变量提升,不可以重复声明
  3. let 声明变量。const声明只读的常量,常量的值不允许更改(只针对基本数据类型)

★数组常用方法

数组map方法
  • return是用来终止函数的
  • for…in最好用来遍历对象

数组常见遍历

  1. for循环
  2. forEach循环,不需要返回,只是单纯的遍历(推荐),一旦开始,无法停止
  3. map循环,必须有返回值,返回的是一个新数组
    • 执行的操作是把原数组中的每个值按照指定的规则映射到新数组中。通过return返回操作后的新数组
const newArr = arr.map(item => {
  return item * item
})
console.log(newArr) // [81, 25, 36, 64, 5929, 4, 9]
数组some方法

参数:

  • item,index,arr
    • item当前项,index索引值,arr调用 some 的当前数组。

返回值:

  • 返回一个判断后的布尔值,需要有return
  • 只要有一个符合要求就为true
const arr = [1,2,3,4,5]
const res = arr.some(item => {
 item > 2
})
console.log(res)  // true
数组every方法

参数:

  • item,index,arr
    • item当前项,index索引值,arr调用 every 的当前数组

返回值:

  • 返回一个判断后的布尔值,需要有return
  • 只要都符合要求才为true
const arr = [1,2,3,4,5]
const result = arr.every(item => {
  return item < 6
})
console.log(result) // true
数组filter方法

参数:

  • callback
    1. item当前项,index索引值,arr调用 filter 的当前数组
  • thisArg

返回值:

  • 返回符合条件的组成的新数组,需要有return
const arr = [1,2,3,4]
const res = arr.filter(item => {
  return item > 2
})
console.log(res) // [3,4]
累加器reduce

参数:

  • callbackFn

    1. pre 累加和
    2. item当前项
    3. index索引值
    4. arr调用 reduce 的当前数组
  • initialValue 初始值

返回值:

  • 返回一个累加后的和,需要有return
const arr = [1,2,3,45]
const res = arr.reduce((pre,item) => {
  return pre += item
},0)
console.log(res) // 51
其他方法
  • findIndex 返回元素对应的索引
  • findLastIndex 返回元素对应的最后的索引

★解构

字符串
const str = 'hello'
const [a,b,c,d,e,f] = str
console.log(a) // h
console.log(f) // undefined
// 结合扩展运算符使用
const str1 = 'helloworld'
const [a,b,..c] = str1
console.log(a) // h
console.log(c) // ['l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
  • 扩展运算符可以快速实现字符串分割
const [...s] = 'helloworld'
console.log(s) // ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
s.reverse().join('')
console.log(s) // ['d', 'l', 'r', 'o', 'w', 'o', 'l', 'l', 'e', 'h']
数组
  • 通过索引值来解构的
扩展运算符
  • 可以实现数组的合并
const arr1 = [1,2,3]
const arr2 = [4,5]
const arr = [...arr1,...arr2]
console.log(arr) // [1,2,3,4,5]
  • 可以实现拷贝数组

    • 如果是一维,相当于深拷贝,不会影响原数组
    const arr = [1,2,3,4]
    const newArr = [...arr]
    console.log(newArr)  // [1,2,3,4]
    newArr[0] = 100
    console.log(newArr) // [100,2,3,4]
    console.log(arr)  // [1,2,3,4]
    
    • 如果是多维,相当于浅拷贝,修改引用类型,会影响原数组
    const arr1 = [1,2,[3,4]]
    const arr2 = [...arr1]
    arr2[2][0] = 99
    console.log(arr2)  // [1, 2, Array(2)] 
    console.log(arr1)  // [1, 2, Array(2)] 
    
对象
  • 通过键来映射的

  • 解构出来对象中的属性和方法

    • 可以通过 :重命名
const obj = {
  uname: '张三',
  age: 24,
  say() {
    console.log('会说话')
  }
}
// 解构对象的属性和方法
const { uname } = obj
console.log(uname)
const { age: newAge } = obj
console.log(newAge)
const { say } = obj
say()
  • 解构整个对象,利用扩展运算符,做的是深拷贝,不影响原对象
// 解构整个对象,利用扩展运算符
const o = { ...obj }
console.log(o)

★箭头函数

  • 箭头函数没有this指向,它的this指向就是上一层作用域的this指向

箭头函数传参

  1. 只有一个参数,可以省略 ( )
let fn = x => {
  return x * x
}
console.log(fn(10)) // 100

返回体

  1. 函数体只有一条语句,可以省略 { } 和 return
let fn = x => x * x
console.log(fn(10)) // 100

★rest参数

  • 又叫剩余参数 …args 不确定多少参数的情况下可以使用
  • 会将多余的变量合成一个数组
  • 参数也可以指定默认值,防止不传入参数
function fn(x = 0,y = 0,...args) {
  console.log(x)   // 1
  console.log(args) // [3,6,9]
}
fn(1,2,3,6,9)
  • 也可以为对象赋默认值
const { uname,age = 24 } = { uname: 'zs' }
console.log(uname) // zs
console.log(age) // 24

新增的数据解构

Symbol
  • 为了解决对象属性名冲突而诞生的新的原始数据类型

  • 代表独一无二的值

  • Symbol值通过 Symbol() 函数生成

  • 对象的属性名现在可以有两种类型

    1. 原来就有的字符串类型 ‘uname’ : ‘张三’
    2. Symbol类型,可以保障不会与其他属性名产生冲突
  • Symbol() 里面可以接收一个字符串参数。也就是属性名,用于区分

应用场景:

  • 对对象进行扩展
    • 如果添加一个,可以直接写在对象中
      • 访问:对象名[Symbol]
    • 如果多个,就只能再外面声明好,再追加
      • 访问:对象名[属性名]
// 如果添加一个,可以直接写在对象中
const obj = {
  // uname : '张三',
  [Symbol] : '张三'
}
console.log(obj[Symbol])  // 张三

// 如果多个,就只能在外面声明好,再追加
const age = Symbol('age')
const gender = Symbol('gender')
obj[age] = 24
obj[gender] = '男'
console.log(obj)
console.log(obj[age]) // 24
Set
  • 很少用,了解

  • 不允许出现重复的值,也是一个构造函数,需要new进行使用

  • 接收数组类型的参数,如果不是,会尝试进行转换为数组,转换不成功,则报错

  • 可以进行解构出来

格式:set(size长度) {元素...}

const s = new Set([1, 2, 3])
console.log(s)  // set(3) {1,2,3}
console.log(s[0]) // undefined
console.log(...s)  // 1,2,3
常用方法

add() 进行添加

  • 一次只能添加一个,如果想添加多个,可以放到数组或者对象里
s.add(4,5)
console.log(s)  // Set(4) {1, 2, 3, 4}
// 一次只能添加一个,如果想添加多个,可以放到数组或者对象里
s.add([6,7,8])
console.log(s)  // Set(5) {1, 2, 3, 4, Array(3)}
s.add({uname: 'zs',age: 24})
console.log(s)  // Set(6) {1, 2, 3, 4, Array(3), …}

delete() 进行删除

  • 返回一个布尔值,表删除成功/失败
  • 如果删除对象,需要用到 delete 操作符(具体怎么删除Set里的对象,待定)
const s = new Set([1, 2, 3])
// delete删除某个值,返回一个布尔值,删除成功/失败
console.log(s.delete(1))  // true
console.log(s)  // Set(2) {2, 3}

has() 是否包含某个值

  • 返回一个布尔值,判断是否存在某个值
const s = new Set([1, 2, 3])
// has判断是否有某个值
console.log(s.has(9))  // false
console.log(s.has(4))  // true

clear()

  • 清空全部
const s = new Set([1, 2, 3])
s.clear()
console.log(s) // Set(0) {size: 0}
Set的遍历
  1. keys() 返回键名的一个遍历器

  2. values() 返回值的一个遍历器

Set的键和值是同一个值,所以 keys() 和 values() 返回的结果是一样的

const s = new Set([1,5,9,4])
console.log(s)  // Set(4) {1, 5, 9, 4}
// 1. keys()  返回键名的遍历器
console.log(s.keys())  // SetIterator {1, 5, 9, 4}
// 2. values()  返回值的遍历器
console.log(s.values())  // SetIterator {1, 5, 9, 4}
  1. entries() 返回键值对映射的遍历器
const s = new Set([1,5,9,4])
// 3. entries()   返回键值对映射的遍历器
console.log(s.entries())  // SetIterator {1 => 1, 5 => 5, 9 => 9, 4 => 4}
  1. for…of 进行遍历,因为 Set 内置了 iterator 接口(只要有iterator接口,就可以使用for…of)
    • Array,String,Map,Set,NodeList,TypeArray,函数的arguments 都内置了iterator
    • 其他类型想要使用iterator,则需要进行配置,才能使用for…of
const s = new Set([1,5,9,4])
// 4. for...of 遍历
for (const iterator of s) {
  console.log(iterator)  // 1  5  9  4
}
  1. Set 自带的forEach进行循环遍历
const s = new Set([1,5,9,4])
// 5. 用Set自带的forEach循环遍历
s.forEach(item => {
  console.log(item)  // 1  5  9  4
})
应用场景

数组去重:

const arr = [1, 2, 6, 6, 6, 9]
let res = new Set(arr)
console.log(res) // Set(4) {1, 2, 6, 9}
// 此时res还是Set数据类型,要把Set转为数组
// 1. Array.from(res)
console.log(Array.from(res))  // [1, 2, 6, 9]
// 2. 扩展运算符
console.log([...res])  // [1, 2, 6, 9]
Map
  • 很少用,了解

  • 也是对对象进行扩展的,对对象进行了升级(原来对象的键只能用字符串,这个的键可以是任何类型)

常用方法

set() 进行添加

  • 使用方法前,要区分简单数据类型和复杂数据类型
  • 存引用数据类型时,最好用一个变/常量接收引用类型,然后直接存入这个变/常量(方便后面取)
格式: Map(size长度) {元素...}

const m = new Map()
console.log(m) // Map(0) {size: 0}
// 1. set()  添加
// 简单数据类型时,会覆盖,因为存放在栈里
m.set(1,'yi')
m.set(1,'one')
console.log(m) // Map(1) {1 => 'one'} 
// 复杂数据类型时,因为内存地址不同,所以会添加两个
m.set([1],'yi')
const arr = [1]
m.set(arr,'one')
console.log(m)  // Map(3) {1 => 'one', Array(1) => 'yi', Array(1) => 'one'}

get() 进行获取

const m = new Map()
m.set(1,'one')
// 复杂数据类型时,因为内存地址不同,所以会添加两个
m.set([1],'yi')
const arr = [1]
m.set(arr,'one')
console.log(m)  // Map(3) {1 => 'one', Array(1) => 'yi', Array(1) => 'one'}
// 2. get() 获取
console.log(m.get(1)) // one
// 引用类型不能直接用定义的键名获取
console.log(m.get([1])) // undefined
// 要提前给引用类型的数据赋值变量,然后把变量存进去,取的时候也用变量名取
console.log(m.get(arr)) // one

delete() 删除

const m = new Map()
m.set(1,'one')
// 复杂数据类型时,因为内存地址不同,所以会添加两个
m.set([1],'yi')
const arr = [1]
m.set(arr,'one')
// 3. delete() 删除
m.delete(1)
console.log(m) // Map(2) {Array(1) => 'yi', Array(1) => 'one'}
m.delete(arr)
console.log(m) // Map(1) {Array(1) => 'yi'}

clear() 清空

const m = new Map()
console.log(m) // Map(0) {size: 0}
m.set(1,'one')
// 4. clear()  清空
m.clear()
console.log(m) // Map(0) {size: 0}
Map的遍历

遍历和Set一样,也可以通过 keys() values() entries() for...of forEach

★数据劫持

  • 又叫数据拦截,数据代理,数据绑定

数据双向绑定原理

vue2 Object.defindeProperty()

vue3 proxy()

区别:

  • Object.defindeProperty() 一次只能监听一个对象的一个属性
  • proxy() 可以监听整个对象
Object.defindeProperty()
语法:Object.defindeProperty(要监听的对象名,新增属性名/对象原有的属性名,{配置项})

配置项里面有多个属性:

  1. value 数据被劫持后,修改后的新值,默认 undefined
  2. writable 布尔值,是否允许再被修改,默认 false
// vue2 响应式原理是 Object.defindeProperty()
const obj = {
  uname: '张三',
  age: 23
}
// defineProperty 有3个参数   要监听的对象名  新增属性名/对象有的属性名  {配置项} 
Object.defineProperty(obj,'uname',{
  // value  劫持属性名后要修改的值
  value: '王二小',
  // writable  布尔值,是否允许被重写
  writable: false,
})
// 因为配置项里不允许再被修改,所以这里修改了也没用
obj.uname = '王五'
console.log(obj)  // {uname: '王二小', age: 23}
  1. get() get方法,只要获取对象中的属性/方法,就会调用这个方法。默认undefined

  2. set() set方法,只要对象中的属性/方法修改,就会调用这个方法。默认undefined

  • set() 存在一个默认参数,就是修改后的新值

注意:get() set() 方法 与 value writable冲突,二选一,不能同时出现

const obj = {
  uname: '张三',
  age: 23
}


// 定义一个过渡值,就可以解决栈内存溢出的问题
let newName = ''
Object.defineProperty(obj, 'uname', {
  // get  get方法,只要获取对象中的属性/方法,就会调用这个方法。默认undefined
  get() {
    // return 'get方法被调用了'
    // return this  // this 指向 obj
    // return this.uname
    return newName
  },
  // set  set方法,只要对象中的属性/方法修改,就会调用这个方法。默认undefined
  // set()  会有一个默认参数,就是修改后的新值
  set(newVal) {
    // set() 劫持到修改后的新值newVal以后,要赋值给原先的属性名吧
    // this.uname = newVal
    // 把newVal赋值给过渡值
    newName = newVal
  }
})
// 修改uanme 会被set方法劫持到,会触发set()
obj.uname = '王五'
// 查看uname的值,会触发get方法 
console.log(obj.uname)
  • 如果要监视对象,则需要把defindeProperty的 第二个参数(新增属性名/对象原有的属性名) 替换为 键名(kes()获取)
  • 然后把代码放入到循环中,每个属性都要有新的中间过渡值
Proxy
  • 对整个对象进行数据代理,是个构造函数,需要new来使用
  • 通过 Proxy() 代理对象后,不能再操作原对象了,而是操作实例化对象
  • 如果配置项不进行任何操作,则相当于直接操作原对象
const obj = {
  uname: '王五',
  age: 23
}
// Proxy()  有两个参数,第一个是要监视的对象名,第二个是配置项(get&set)
const p = new Proxy(obj,{
  // 获取属性的时候触发,有3个参数  
  // target:目标对象   propKey:目标对象的属性   recevier:可选 实例化对象
  get: function(target, propKey, recevier) {
    // console.log(target)
    // console.log(propKey)
    return target[propKey]
  },
  // 设置属性的时候触发,有4个属性,比get多了一个value
  // value:设置的新值
  set: function(target, propKey, value, recevier) {
    target[propKey] = value
  }
})

// 原对象调用是不会触发get和set的,因为原对象已经交给了Proxy()进行代理
// console.log(obj.uname)  // 王五

// 所以应该使用Proxy()进行调用
p.age = 24
console.log(p.age) // 24
p.uname = '罗翔'
console.log(p.uname) // 罗翔

★Promise对象

  • Promise也是一个构造函数,需要new来使用
  • 实例化的Promise(),里面接收一个函数 new Promise(() => {…})
  • 函数可以接收两个参数,一个是成功回调(resolve()),一个是失败回调(reject(),可选)
    • 成功回调(resolve()) 会自动调用then方法
      • resolve() 里面也可以接收参数,就是请求成功后返回的数据
    • 失败回调(reject()) 会自动调用catch方法
      • reject() 里面也可以接收参数,就是请求失败后返回的提示信息
const p = new Promise((resolve,reject) => {
  setTimeout(() => {
    // resolve('请求成功')
    reject('请求失败')
  },2000)
}).then(res => {
  console.log(res) // 请求成功
}).catch(err => {
  console.log(err) // 请求失败
})

console.log(p)
promise调用状态
  1. fulfilled 表成功
  2. pending 表进行中 可以过渡到成功或者失败的状态
  3. reject 表失败
  • 一旦状态确定后,就不再改变了
Promise.all()与Promise.race()

promise.all()

  • 全部异步操作都成功,则组合成数组。只要有一个失败,则返回失败的回调
  • 应用场景: 网页必须等待所有异步请求都完成后才能展示
  • 接收参数:promise组成的数组
<body>
  <script>
    // 模拟几个异步请求
    const p1 = new Promise((resolve,reject) => {
      setTimeout(() => {
        // resolve('调用成功')
        reject('调用失败')
      },1000)
    })
    // console.log(p1)
    const p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('调用成功')
      }, 2000)
    })
    // console.log(p2)
    const p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('调用成功')
        // reject('调用失败')
      }, 3000)
    })
    // console.log(p3)

    // Promise.all()  全部成功回调后,合成成功的回调数组。有一个失败的,就返回失败的
    const p = Promise.all([p1,p2,p3])
    console.log(p)
    // Promise.race()  不管成功失败,返回最先返回的回调状态
    const pp = Promise.race([p1, p2, p3])
    console.log(pp)
  </script>
</body>

面试题:Promise.all()与Promise.race() 的 异同

同:

  • 接收的参数都是 promise 组成的数组

异:

  • promise.all() 全部异步操作都成功,则组合成数组。只要有一个失败,则返回失败的回调
  • 而promise.race() 则是不管成功还是失败,只要那个异步操作先完成,则返回这个异步操作的状态(成功/失败)

★async与await

  • es7推出的回调地狱的解决方案
  • async与await成双成对出现,await写在async修饰的函数里
  • await 代替了then的工作
  • async修饰的函数,里面的异常只能通过 try-catch 捕获
<body>
  <button>get请求</button>
  <button class="post">post请求</button>
  <script>
    // get
    document.querySelector('button').addEventListener('click',async function() {
      const { data: res } = await axios({
        method:'get',
        url: 'https://autumnfish.cn/api/joke',
        params: {
          num: 1
        }
      })
      console.log(res)
    })
    // post
    document.querySelector('.post').addEventListener('click',async function() {
      try {
        const { data: res } = await axios({
          method: 'post',
          url: 'https://autumnfish.cn/api/joke',
          data: {
            num: 1
          }
        })
        console.log(res)
      } catch (error) {
        console.log(error)
      }
    })
  </script>
</body>

★类class

  • 私有属性要写到constructor构造器里面
  • 共有方法/属性直接在在外面,会挂载到原型对象prototype上
  • 如果不写constructor构造器,则会默认给添加一个空的构造器
class Father {
  // 私有属性
  constructor(uname,age) {
    this.uname = uname
    this.age = age
  }
  // 共有属性/方法
  gender = '男'
  say() {
    console.log('总是训斥儿子')
  }
}
const f = new Father('王五',55)
console.log(f) // Father {gender: '男', uname: '王五', age: 55}
f.say()  // 总是训斥儿子
console.log(f.gender) // 男
继承
  • 使用extends进行继承,可以继承父类的所有属性和方法
  • 子类要有自己的私有属性,必须调用super()
  • 子类也可以重写父类的方法
  • 子类也可以添加自己特有的方法
// 创建一个子类,继承父类,可以使用父类的方法和属性
class Son extends Father {
  constructor(uname,age,gender) {
    // 子类要有自己的私有属性,必须调用super()
    super(uname,age)
    this.gender = gender
  }
  // 子类可以添加自己特有的方法
  study() {
    console.log('儿子努力学习')
  }
  // 子类也可以重写父类的方法
  say() {
    console.log('儿子希望父亲能和蔼一点')
  }
}
const s = new Son('张三',24,'男')
console.log(s)  // Son {gender: '男', uname: '张三', age: 24}
s.say()  // 儿子希望父亲能和蔼一点
s.study()  // 儿子努力学习
静态方法
  • static修饰的方法,不会被继承,它只属于当前类,只有当前类才能调用
  • 被static修饰的共有属性,也是不会被继承的,只有当前类才能调用
    • 注意:是当前类,不是类的实例化
class Person {
  constructor(uname,age) {
    this.uname = uname
    this.age = age
  }
  // static 修饰的方法和属性,都不会被继承,只能通过此类调用
  static gender = '男'
  static say() {
    console.log('会说话')
  }
}
console.log(Person.gender) // 男
Person.say() // 会说话

class zs extends Person {
  constructor(uname,age) {
    super(uname, age)
  }
}
const z = new zs('张三',24) 
console.log(z) // // zs {uname: '张三', age: 24}

iterator接口

  • Array,String,Map,Set,NodeList,TypeArray,函数的arguments 都内置了iterator

  • 只要有iterator接口,就可以使用 for…of 方法进行循环

  • 其他类型想要使用iterator,则需要进行配置,才能使用for…of

// Array,String,Map,Set,NodeList,TypeArray,函数的arguments 都内置了iterator
// 可以直接使用 for...of 进行循环
// const arr = [1,5,9,10]
// for (const iterator of arr) {
//   console.log(iterator)
// }
// const str = 'helloworld'
// for (const iterator of str) {
//   console.log(iterator)
// }
// console.log(arr)

// 其他类型想要使用iterator,则需要进行配置,才能使用for...of
const obj = {
  uname: '张三',
  age: 24,
  // 进行iterator配置
  [Symbol.iterator]() {
    const that = this
    // console.log(that)  // 此时that就是obj
    let index = 0
    const data = Object.keys(that)   // 返回一个键组成的新数组
    // console.log(data)
    return {
      next() {
        if (index < data.length) {
          return {
            value: data[index++],
            done: false
          }
        }
        return { value: undefined, done: true }
      }
    }
  }
}

console.log(obj)

for (const k of obj) {
  // 键
  console.log(k)
  // 值
  console.log(obj[k])
}

★模块化

  • 模块,就是一个独立的js文件

  • 模块导出 export 模块导入import

script标签的type属性,可以定义模块。定义模块后,浏览器会将其识别为一个模块

<script type="module">...</script>

分别导出

  • 可以解构导入,也可以全部导入
.js文件(模块)
export let hero = '维克托'
export function work() {
  console.log('发明家');
}

.html文件
<script type="module">
  // 可以解构引入
  // import { hero,work } from './module/01-分别暴露.js'
  // console.log(hero)  // 维克托
  // work()  // 发明家
  
  // 也可以全部引入
  import * as h from './module/01-分别暴露.js'
  console.log(h) // Module {Symbol(Symbol.toStringTag): 'Module'}
  h.work()  // 发明家
</script>

统一导出又叫批量导出

  • export { … } 里面一定不要加括号
  • 可以使用解构导入
.js文件(模块)
const num = 10
function fn() {
  console.log('77777')
}
export { num, fn }

.html文件
<script type="module">
  // 解构导入
  import { num,fn } from './module/03-批量导出.js'
  console.log(num) // 10
  fn() // 77777
</script>

默认导出

  • 一个模块只能有唯一一个默认的导出模块
  • export default { … } 不能使用解构来导入,也不能使用全部导入
  • 只能使用 import 导入名 from '...' 导入
.js文件(模块)
// 一个模块只能有一个默认导出
export default {
  uname: '张三',
  age: 23,
  say() {
    console.log('会普法')
  },
}

.html文件
<script type="module">
  // 引入默认模块,不需要解构,也不能全部导入,只能通过 import 导入名 from '...' 导入
  import person from './02-默认导出.js'
  console.log(person) // {uname: '张三', age: 23, say: ƒ}
  person.say() // 会普法
</script>

模块引入

  1. 整体引入
import * as 导入名 from '....'
  1. 结构赋值形式
import { 属性名[, 方法名...]} from '...'
  1. 默认导出的引入
import { default as 导入名 } from '...'
  1. 简便型式,针对默认暴露
import 导入名 from '...'

补充

对象简写
const uname = '张三'
const age = 23
const obj = {
  uname: uname,
  // 1. 键名和值名都相同的情况下,可以简写成一个
  age,
  // 2. 函数简写
  // 老版
  sing: function() {
    console.log('唱')
  },
  // 简写
  dance() {
    console.log('跳')
  }
}
console.log(obj) // {uname: '张三', age: 23, sing: ƒ, dance: ƒ}
obj.sing() // 唱
obj.dance() // 跳
栈内存溢出报错
Uncaught RangeError: Maximum call stack size exceeded

栗子

<body>
  <script>
    const obj = {
      uname: '张三',
      age: 23
    }
    Object.defineProperty(obj, 'uname', {
      // get  get方法,只要获取对象中的属性/方法,就会调用这个方法。默认undefined
19get() {
        // return 'get方法被调用了'
        // return this  // this 指向 obj
22return this.uname
      },
      // set  set方法,只要对象中的属性/方法修改,就会调用这个方法。默认undefined
      // set()  会有一个默认参数,就是修改后的新值
      set(newVal) {
        // set() 劫持到修改后的新值newVal以后,要赋值给原先的属性名吧
28this.uname = newVal
      }
    })
    // 修改uanme 会被get方法劫持到
    obj.uname = '王五'
33行 console.log(obj.uname)
    // 为什么22行+28行的this写法会造成栈内存溢出?
    // 因为:代码执行到33行时,要查看obj的uname属性,所以会调用19行的get方法
    // 而19行的get方法,返回值又是 obj.uname 又是要查看obj的uname属性
    // 所以会接着调用19行的get方法,反复调用,然后就陷入了死循环
  </script>  
</body>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值