对象Object

如何证明一个属性是可枚举的?

  • for…in 可遍历的就是可枚举的(不包含symbol,包含继承的属性)
  • obj.hasOwnProperty 是用来查询是否是自身的属性(包含自身不可枚举,不包含继承)
  • obj.propertyIsEnumerable 是用来查询自身的属性是否可枚举(包含symbol)

遍历对象

  • for...in - 遍历自身、继承的可枚举属性(不含Symbol)
  • Object.keys() - 仅遍历自身可枚举属性(不含Symbol)
  • Object.getOwnPropertyNames() - 遍历自身的可枚举属性和不可枚举属性

Generator 生成器

一个函数本来只能有一个return 返回值。但是yield可以让函数执行中有多次的返回。可以暂停函数执行。

示例1 - 自增ID:

要生成一个自增的ID,可以编写一个next_id()函数:

  1. var current_id = 0;
  2. function next_id() {
  3. current_id ++;
  4. return current_id;
  5. }

由于函数无法保存状态,故需要一个全局变量current_id来保存数字。
不用闭包,试用generator改写:

  1. function* next_id() {
  2. var i = 1;
  3. while(true) yield i++;
  4. }

示例2 - 斐波那契数列:

  1. function* fib(max) {
  2. var a = 0,
  3. b = 1,
  4. n = 0
  5. while (n < max) {
  6. yield a
  7. ;[a, b] = [b, a + b]
  8. n++
  9. }
  10. return
  11. }
  12. for (var x of fib(10)) {
  13. console.log(x) // 依次输出0, 1, 1, 2, 3, ...
  14. }

JS函数柯里化(Currying)的概念

概念:

**

柯里化(Currying),是把接收多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回接受剩余的参数而且返回结果的新函数的技术。

常见作用:

  1. 参数复用 - 如:curryingAdd(1)(2)
  2. 提前返回
  3. 延迟计算/运行

手写 - Promise

Promise/A+规范:

  • 有三种状态 pending | resolved | rejected
  • 处于 pending 时,可以转移到 resolved 或者 rejected 状态。
  • 处于 resolvereject 时,就不可变。
  • 必须有一个 then 异步执行方法,then 接受两个参数且必须返回一个 promise

    流程图:

    169c500344dfe50a.jpg

    代码:

    ```javascript function myPromise(constructor){ let self=this; self.status=”pending” //定义状态改变前的初始状态 self.value=undefined;//定义状态为resolved的时候的状态 self.reason=undefined;//定义状态为rejected的时候的状态 function resolve(value){
    1. //两个==="pending",保证了状态的改变是不可逆的
    2. if(self.status==="pending"){
    3. self.value=value;
    4. self.status="resolved";
    5. }
    } function reject(reason){
    1. //两个==="pending",保证了状态的改变是不可逆的
    2. if(self.status==="pending"){
    3. self.reason=reason;
    4. self.status="rejected";
    5. }
    } //捕获构造异常 try{
    1. constructor(resolve,reject);
    }catch(e){
    1. reject(e);
    } }

// 同时,需要在myPromise的原型上定义链式调用的then方法: myPromise.prototype.then=function(onFullfilled,onRejected){ let self=this; switch(self.status){ case “resolved”: onFullfilled(self.value); break; case “rejected”: onRejected(self.reason); break; default:
} }

  1. <a name="1TIHX"></a>
  2. # 手写 - 防抖(debounce)
  3. <a name="MlvUG"></a>
  4. ## 原理:
  5. 函数防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。(例:监听input输入框值的变化,用非立即执行)
  6. - 立即执行:多次触发事件,第一次会立即执行函数,之后在设定wait时间内触犯的事件无效,不会执行。
  7. - 非立即执行:多次触发事件,只会在最后一次触发事件后等待设定的wait时间结束时执行一次。
  8. <br />
  9. <a name="s0R3A"></a>
  10. ## 代码:
  11. ```javascript
  12. function debounce(fn, wait = 50, immediate) {
  13. let timer
  14. return function () {
  15. // timer不为null,清除定时器
  16. if (timer) clearTimeout(timer)
  17. if (immediate) {
  18. // timer为null时,可以执行
  19. if (!timer) fn.apply(this, arguments)
  20. // 在 wait 时间后将timer 变为null,代表可以执行
  21. timer = setTimeout(() => {
  22. timer = null
  23. }, wait)
  24. } else {
  25. timer = setTimeout(() => {
  26. fn.apply(this, arguments)
  27. }, wait)
  28. }
  29. }
  30. }

手写 - 节流(throttle)

原理:

指定时间间隔内只会执行一次任务。(例:滚动条监听等)

代码:

  1. function throttle(fn, wait) {
  2. let prev = new Date()
  3. return function () {
  4. const now = new Date()
  5. if (now - prev > wait) {
  6. fn.apply(this, arguments)
  7. prev = now
  8. }
  9. }
  10. }

手写 - call、apply、bind

call、apply、bind原理

代码示例:

  1. var Tom = {
  2. name: 'tom',
  3. age: 16,
  4. fn: function (from, to) {
  5. console.log(`name: ${this.name}, age: ${this.age}; from: ${from}, to: ${to}`)
  6. },
  7. }
  8. var Eric = {
  9. name: 'eric',
  10. age: 18,
  11. }
  12. Tom.fn('成都', '上海') // name: tom, age: 16; from: 成都, to: 上海
  13. Tom.fn.call(Eric, '成都', '上海') // name: eric, age: 18; from: 成都, to: 上海
  14. Tom.fn.apply(Eric, ['成都', '上海']) // name: eric, age: 18; from: 成都, to: 上海
  15. Tom.fn.bind(Eric, '成都', '上海')() // name: eric, age: 18; from: 成都, to: 上海

相同:

  • 三者都可以让一个对象去使用其他对象中的方法,并将 this 指向当前对象。
  • 第一个参数都是this的指向对象。不传

    不同:

  • call:参数如同普通函数的传参,从第二个参数开始。直接返回函数调用结果。

  • apply:与 call 相似,但参数必须以数组的形式,传入第二个参数中。 直接返回函数调用结果。
  • bind:参数传参形式与 call 相同。返回值是一个函数,需要再次调用。bind 实现还需要考虑实例化后对原型链的影响。

实现 call()

  1. /**
  2. * 1. 创建一个独一无二的key,避免key值冲突
  3. * 2. 将函数挂载在obj上
  4. * 3. 调用,获取返回值。传参如同普通函数调用,一个个传入。
  5. * 4. 删除key
  6. * 5. return 返回值
  7. */
  8. Function.prototype.myCall = function (obj, ...args) {
  9. const key = Symbol()
  10. obj[key] = this
  11. const res = obj[key](...args)
  12. delete obj[key]
  13. return res
  14. }
  15. Tom.fn.myCall(Eric, '成都', '上海') // name: eric, age: 18; from: 成都, to: 上海

实现 apply()

  1. Function.prototype.myApply = function (obj) {
  2. // 逻辑上与 call 一致,仅传参有区别。
  3. const key = Symbol()
  4. obj[key] = this
  5. // 判断是否有第二个参数,以数组形式接收,正常参数列表传参。
  6. let res
  7. if (arguments[1]) {
  8. res = obj[key](...arguments[1])
  9. } else {
  10. res = obj[key]()
  11. }
  12. delete obj[key]
  13. return res
  14. }
  15. Tom.fn.myApply(Eric, ['成都', '上海']) // name: eric, age: 18; from: 成都, to: 上海

实现 bind()

  1. Function.prototype.myBind = function (obj, ...bindArgs) {
  2. // 接收参数类似call。返回一个函数。还要考虑实例化后对原型链的影响
  3. // 不是function要抛出错误
  4. if (typeof this != 'function') {
  5. throw Error('not a function')
  6. }
  7. let fn = this
  8. function resFn(...resArgs) {
  9. // 若当前函数this指向的是构造函数中的this,则判定为new操作。
  10. const resThis = this instanceof resFn ? this : obj
  11. // 接收参数,bind时绑定或此函数传参
  12. return fn.call(resThis, ...bindArgs, ...resArgs)
  13. }
  14. // 维持原型关系,避免直接prototype互相赋值,用空函数来中继
  15. function tmp() {}
  16. tmp.prototype = fn.prototype
  17. resFn.prototype = new tmp()
  18. return resFn
  19. }
  20. Tom.fn.myBind(Eric, '成都', '上海')() // name: eric, age: 18; from: 成都, to: 上海

手写 - new

  1. /**
  2. * new 做的事情
  3. * 1. 创建一个全新的对象。
  4. * 2. 执行 prototype 链接。
  5. * 3. 使 this 指向新创建的对象。
  6. * 4. 如果函数没有返回对象类型(含Functoin, Array, Date, RegExg, Error),
  7. * 那么 new 将返回该对象引用。
  8. */
  9. function New(fn) {
  10. var obj = {
  11. __proto__: fn.prototype,
  12. }
  13. var res = fn.apply(obj, Array.prototype.slice.call(arguments, 1))
  14. if (res !== null && (typeof res === 'object' || typeof ret === 'function')) {
  15. return res
  16. }
  17. return obj
  18. }
  19. // 使用示例
  20. function Person(name) {
  21. this.name = name
  22. }
  23. const Jim = new Person('Jim') // Person {name: 'Jim'}
  24. const Bob = New(Person, 'Bob') // Person {name: 'Bob'}

手写 - instanceOf

instanceOf 用来判断一个对象的 prototype 是否在另外另外一个对象的原型链上。

  1. function myInstanceOf(left, right) {
  2. const rightProto = right.prototype
  3. let leftProto = left.__proto__
  4. while (true) {
  5. // 所有 __proto__ 都检测完了,不在原型链上,返回false
  6. if (leftProto === null) return false
  7. // 在原型链上,返回true
  8. if (leftProto === rightProto) return true
  9. // 往上层继续查找
  10. leftProto = leftProto.__proto__
  11. }
  12. }
  13. myInstanceOf({}, Object) // true

手写 - 深拷贝

丐版:

  1. function deepClone(obj) {
  2. return JSON.parse(JSON.stringify(obj))
  3. }

面试够用版:

  1. function deepClone(obj) {
  2. // 判断是否为复杂类型,兼容数组,复杂类型递归调用
  3. if (typeof obj === 'object') {
  4. var res = obj.constructor === Array ? [] : {}
  5. for (const key in obj) {
  6. res[key] = deepClone(obj[key])
  7. }
  8. return res
  9. }
  10. // 简单类型,直接赋值
  11. return obj
  12. }

实现一个继承

寄生组合式继承:

  1. // 定义父类 Animal
  2. function Animal(name) {
  3. this.name = name
  4. this.sleep = function () {
  5. console.log(this.name + '正在睡觉!')
  6. }
  7. }
  8. Animal.prototype.eat = function (food) {
  9. console.log(this.name + '正在吃:' + food)
  10. }
  11. // 定义子类 Cat
  12. function Cat(name, color) {
  13. // 组合,继承父类构造函数的属性,可传参
  14. Animal.call(this, name)
  15. this.color = color
  16. this.show = function () {
  17. console.log(`${this.name}是${this.color}色的`)
  18. }
  19. }
  20. // 寄生
  21. function extendProto(child, parent) {
  22. // 创建一个空的函数,用以继承父类原型,同时又不会继承实例属性。
  23. function F() {}
  24. F.prototype = parent.prototype
  25. child.prototype = new F()
  26. // 修复实例
  27. child.prototype.constructor = child
  28. }
  29. extendProto(Cat, Animal)
  30. // 实例化
  31. var cat = new Cat('Tom', '白')
  32. console.log(cat.name) // Tom
  33. console.log(cat.color) // 白
  34. cat.sleep() // Tom正在睡觉!
  35. cat.show() // Tom是白色的
  36. cat.eat('鱼') // Tom正在吃:鱼
  37. console.log(cat instanceof Animal) // true
  38. console.log(cat instanceof Cat) //true

ES6继承:

  1. // 创建父类 Animal
  2. class Animal {
  3. constructor(name) {
  4. this.name = name
  5. }
  6. sleep() {
  7. console.log(`${this.name}正在睡觉!`)
  8. }
  9. eat(food) {
  10. console.log(`${this.name}正在吃${food}`)
  11. }
  12. }
  13. // 创建子类 Cat
  14. class Cat extends Animal {
  15. constructor(name, color) {
  16. super(name)
  17. this.color = color
  18. }
  19. show() {
  20. console.log(`${this.name}是${this.color}色的`)
  21. }
  22. }
  23. // 实例化
  24. var cat = new Cat('Tom', '白')
  25. console.log(cat.name) // Tom
  26. console.log(cat.color) // 白
  27. cat.sleep() // Tom正在睡觉!
  28. cat.show() // Tom是白色的
  29. cat.eat('鱼') // Tom正在吃:鱼
  30. console.log(cat instanceof Animal) // true
  31. console.log(cat instanceof Cat) //true