JS高级总结

177 阅读10分钟

image-20211103211000907

image-20211103210551578

image-20211103210758900

image-20211103211340901

image-20211103213156396

image-20211103213156396

image-20211103214410056

image-20211103221211806

image-20211103221255368

image-20211103224757028

image-20211106221200604

image-20211107225034134

image-20211107225609604

image-20211107230016085

image-20211107230338966

image-20211107230441177

作用域

image-20211106084726379

image-20211106090438785

image-20211106131323364

进入执行上下文时候的变量对象

image-20220101234459713

闭包是如何形成的

// 闭包是由两部分组成的:函数 + 可以访问的自由变量
function foo() {
    var name = 'foo'
    function bar() {
        console.log('bar', name)
    }
    return bar
}
var fn = foo()
fn() // 'bar' foo

// 闭包的理解:一个函数可以访问外层作用域的自由变量得时候,那么这个函数就是一个闭包
// 广义的角度来说:JavaScript中的函数都是闭包
// 狭义的角度来说:JavaScript中一个函数如果访问了外层作用域中的自由变量,那么这个函数就是一个闭包

内存泄露

image-20211113232319687

闭包中对自由变量的销毁

// 如果没有引用的自由变量会被js引擎进行销毁
function foo() {
var name = 'kobe'
var age = 18
function bar() {
  debugger
  console.log(name)
}
return bar
}

var fn = foo()
fn()

间接函数引用

image-20220101174624626

this的指向

  // 默认绑定优先级最低 fn()
  // 显示绑定优先级高于隐式绑定
  // new优先级高于隐式绑定
  let obj = {
    age: 20,
    foo: function () {
      console.log(this)
    }
  }
  // call/apply
  obj.foo.call('abc')
  // bind
  obj.foo.bind('ccb')()
  // new优先级高于隐式绑定
  let f = new obj.foo()
  // new优先级高于显示绑定
  // new > 显示绑定 > 隐式绑定 > 默认绑定

箭头函数this的指向

面试题this

//面试题1
  const name = 'window';
  const person = {
    name: 'person',
    sayName: function () {
      console.log(this.name)
    }
  };

  function sayName() {
    const sss = person.sayName;
    sss(); // window
    person.sayName(); // person
    (person.sayName)(); // person
    (b = person.sayName)(); // window
  }
  sayName()
  
  
  // 面试题2
  const name = 'window'
  const person1 = {
    name: 'person1',
    foo1: function () {
      console.log(this.name)
    },
    foo2: () => {
      console.log(this.name)
    },
    foo3: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo4: function () {
      return () => {
        console.log(this.name)
      }
    }
  }

  const person2 = {name: 'person2'}

  person1.foo1() //person1
  person1.foo1.call(person2) //person2

  person1.foo2() // window
  person1.foo2.call(person2) // window

  person1.foo3()() // window
  person1.foo3.call(person2)() // window
  person1.foo3().call(person2) // person2

  person1.foo4()() // person1
  person1.foo4.call(person2)() // person2
  person1.foo4().call(person2) // person1
  
    // 面试题3
  const name = 'window'
  function Person(name) {
    this.name = name
    this.foo1 = function () {
      console.log(this.name)
    }
    this.foo2 = () => {
      console.log(this.name)
    }
    this.foo3 = function () {
      return function () {
        console.log(this.name)
      }
    }
    this.foo4 = function () {
      return () => {
        console.log(this.name)
      }
    }
  }

  let person1 = new Person('person1')
  let person2 = new Person('person2')

  person1.foo1() // person1
  person1.foo1.call(person2) // person2

  person1.foo2() // person1
  person1.foo2.call(person2) // person1

  person1.foo3()() // window
  person1.foo3.call(person2)() // window
  person1.foo3().call(person2) // person2

  person1.foo4()() // person1
  person1.foo4.call(person2)() // person2
  person1.foo4().call(person2) // person1
  

面试题4

image-20211116212945650

// js额外知识
const obj = {
  name: '张三',
  age: 20,
  message: 'hello World'
}
// with可以制造一个作用域
with (obj) {
  console.log(message)
}

// eval 可以执行一个js代码

继承

原型链继承

// 原型链继承
function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype.studying = function () {
  console.log('studying')
}

function Student() {
  this.sco = 100
}

// 原型链继承
// 弊端 所有的属性都在p上
// 1.打印对象看不到属性 2.属性被多个对象共享,如果是引用类型会相互影响  3.不能够传递参数
Student.prototype = new Person()

Student.prototype.eating = function () {
  console.log('eating')
}

借用构造函数继承

// 组合继承
function Person(name, age, friends) {
  this.name = name
  this.age = age
  this.friends = friends
}


function Student(name, age, friends, sno) {
  // 借用构造函数继承
  Person.call(this, name, age, friends)
  this.sno = 200
}
// 借用构造函数继承
Student.prototype = new Person()

Student.prototype.eating = function () {
  console.log('eating')
}

let stu1 = new Student('张三', 20, '李四')
let stu2 = new Student('张三', 30, '王五')
console.log(stu1)
console.log(stu2)

// 弊端:
// 1.Person至少会被调用两次
// 2.stu的原型对象上会多出一些属性,但是这些属性没有存在的必要

image-20211217082017350

原型式继承

// 原型式继承
let obj = {
  name:'张三',
  age: 20
}

// 传入的对象是返回对象的原型
// 方法1
function createObject1(o) {
  let newObj = {}
  Object.setPrototypeOf(newObj, o)
  return newObj
}

// 方法2
function createObject2(o) {
  function Fn(){}
  Fn.prototype = o
  let newObj = new Fn()
  return newObj
}

// 方法3
// info的原型已经是obj了
let info = Object.create(obj)
console.log(info.__proto__)

寄生组合式继承

// 寄生组合式继承
function createObject(o) {
  function Fn(){}
  Fn.prototype = o
  return new Fn()
}
// 核心代码
function inheritPrototype(subType, superType) {
  subType.prototype = createObject(superType.prototype)
  // subType.prototype.constructor = subType
  //或者
  Object.defineProperty(subType.prototype, 'constructor', {
    value: subType,
    configurable: true,
    enumerable: true,
    writable: true
  })
}

ES6转为ES5的源码

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  running() {
    console.log('running', this)
  }
}

class Student extends Person {
  constructor(name, age, friends) {
    super(name, age);
  }

  studying() {
    console.log('studying', this)
  }
}

let stu = new Student('why', 20, ['李四'])
console.log(stu)


"use strict";

function _typeof(obj) {
  "@babel/helpers - typeof";
  return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
    return typeof obj;
  } : function (obj) {
    return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  }, _typeof(obj);
}

// 寄生组合式继承
function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  // 寄生组合式继承
  Object.defineProperty(subClass, "prototype", {
    value: Object.create(superClass && superClass.prototype, {
      constructor: {
        value: subClass,
        writable: true,
        configurable: true
      }
    }), writable: false
  });
  // 静态类(方法)的继承
  if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
  _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
    o.__proto__ = p;
    return o;
  };
  return _setPrototypeOf(o, p);
}

// 通过创建父类的实例原型constructor指向子类来创建子类this对象
function _createSuper(Derived) {
  var hasNativeReflectConstruct = _isNativeReflectConstruct();
  return function _createSuperInternal() {
    var Super = _getPrototypeOf(Derived), result;
    if (hasNativeReflectConstruct) {
      var NewTarget = _getPrototypeOf(this).constructor;
      // 通过创建Super实例,实例原型的constructor指向NewTarget
      result = Reflect.construct(Super, arguments, NewTarget);
    } else {
      result = Super.apply(this, arguments);
    }
    return _possibleConstructorReturn(this, result);
  };
}

function _possibleConstructorReturn(self, call) {
  if (call && (_typeof(call) === "object" || typeof call === "function")) {
    return call;
  } else if (call !== void 0) {
    throw new TypeError("Derived constructors may only return object or undefined");
  }
  return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
  if (self === void 0) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  return self;
}

function _isNativeReflectConstruct() {
  if (typeof Reflect === "undefined" || !Reflect.construct) return false;
  if (Reflect.construct.sham) return false;
  if (typeof Proxy === "function") return true;
  try {
    Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {
    }));
    return true;
  } catch (e) {
    return false;
  }
}

function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
    return o.__proto__ || Object.getPrototypeOf(o);
  };
  return _getPrototypeOf(o);
}

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  Object.defineProperty(Constructor, "prototype", {writable: false});
  return Constructor;
}

var Person = /*#__PURE__*/function () {
  function Person(name, age) {
    _classCallCheck(this, Person);

    this.name = name;
    this.age = age;
  }

  _createClass(Person, [{
    key: "running",
    value: function running() {
      console.log('running', this);
    }
  }]);

  return Person;
}();

var Student = /*#__PURE__*/function (_Person) {
  _inherits(Student, _Person);

  // 通过创建Person的实例,实例的原型constructor指向Student
  var _super = _createSuper(Student);

  function Student(name, age, friends) {
    _classCallCheck(this, Student);

    return _super.call(this, name, age);
  }

  _createClass(Student, [{
    key: "studying",
    value: function studying() {
      console.log('studying', this);
    }
  }]);

  return Student;
}(Person);

多态


class Shape {
  getArea() {

  }
}

class RectShape extends Shape{
  getArea() {
    return 100
  }
}

class Circle extends Shape{
  getArea() {
    return 200
  }
}

let r = new RectShape()
let c = new Circle()

// 多态:当不同的类型执行同一个操作,如果表现的行为,形态不一样,那就是多态的表现
function caclArea(shape: Shape) {
  console.log(shape.getArea())
}
caclArea(r)
caclArea(c)


强引用和弱引用

// GC -> Garbage Collection
// 强引用就是当没有引用指向的时候会被GC回收掉,弱引用是GC不把弱引用当回事, 当对象没有除了弱引用之外的引用的话,GC会把对象进行回收

image-20211220222145995

ES12

// 垃圾回收时候提醒销毁(不定时的)
const finalizationRe = new FinalizationRegistry((value) => {
  console.log('注册在FinalizationRegistry的对象,某一个被回收', value)})

let obj = {name: 'why'}
let info = {name: 'kobe'}
// 不定时回收
finalizationRe.register(obj, 'obj')
finalizationRe.register(obj, 'obj')

obj = null
info = null

手写Promise

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULLFILLED = 'fullfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

function executeFunWithCatcherError(exeFn, value, resolve, reject) {
  try {
    const result = exeFn(value)
    resolve(result)
  } catch (err) {
    reject(err)
  }
}

class MyPromise {
  constructor(executor) {
    this.status = PROMISE_STATUS_PENDING
    this.value = undefined
    this.reason = undefined

    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    const resolve = (value) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if (this.status !== PROMISE_STATUS_PENDING) return
          this.status = PROMISE_STATUS_FULLFILLED
          this.value = value
          // console.log('resolve被调用')
          this.onFulfilledCallbacks.forEach(fn => {
            fn(this.value)
          })
        })
      }
    }

    const reject = (reason) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if (this.status !== PROMISE_STATUS_PENDING) return
          this.status = PROMISE_STATUS_REJECTED
          this.reason = reason
          // console.log('reject被调用')
          this.onRejectedCallbacks.forEach(fn => {
            fn(this.reason)
          })
        })
      }
    }

    executor(resolve, reject)
  }

  then(onfullfilled, onrejected) {

    // 在catch中调用的then返回的是一个新的Promsie
    onrejected = onrejected || (err => {throw err})
    onfullfilled = onfullfilled || (value => value)
    // 多个then需要将函数放入数组中

    // then返回的是一个promise
    return new MyPromise((resolve, reject) => {
      // 判断状态,如果不是pending直接执行
      if (this.status === PROMISE_STATUS_FULLFILLED && onfullfilled) {
        executeFunWithCatcherError(onfullfilled, this.value, resolve, reject)
      }

      if (this.status === PROMISE_STATUS_REJECTED && onrejected) {
        executeFunWithCatcherError(onrejected, this.reason, resolve, reject)
      }

      if (this.status === PROMISE_STATUS_PENDING) {
        this.onFulfilledCallbacks.push(() => {
          // try {
          //   const value = onfullfilled(this.value)
          //   resolve(value)
          // } catch (err) {
          //   reject(err)
          // }
          if (onfullfilled)executeFunWithCatcherError(onfullfilled, this.value, resolve, reject)
        })
        this.onRejectedCallbacks.push(() => {
          // try {
          //   const reason = onrejected(this.reason)
          //   resolve(reason)
          // } catch (err) {
          //   reject(err)
          // }
          if (onrejected)executeFunWithCatcherError(onrejected, this.reason, resolve, reject)
        })
      }
    })
  }

  catch(onrejected) {
    // 返回的是一个新的Promise
    return this.then(undefined, onrejected)
  }
  finally(onFinally) {
    this.then(() => {
      onFinally()
    }, () => {
      onFinally()
    })
  }
  static resolve(value) {
    return new MyPromise((resolve, reject) => {
      resolve(value)
    })
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      let values = []
      promises.forEach(promise => {
        promise.then(res => {
          values.push(res)
          if (values.length === promises.length) {
            resolve(values)
          }
        }, err => {
          reject(err)
        })
      })
    })
  }

  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      let values = []
      promises.forEach(promise => {
        promise.then(res => {
          values.push({status: PROMISE_STATUS_FULLFILLED, value: res})
          if (values.length === promises.length) {
            resolve(values)
          }
        }).catch(err => {
          values.push({status: PROMISE_STATUS_REJECTED, reason: err})
          if (values.length === promises.length) {
            resolve(values)
          }
        })
      })
    })
  }
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => {
        promise.then(res => {
          resolve(res)
        }, err => {
          reject(err)
        })
      })
    })
  }

  static any(promises) {
    return new MyPromise((resolve, reject) => {
      // 只能等到一个成功的结果
      // 如果全部是错误的才执行reject
      let reasons = []
      promises.forEach(promise => {
        promise.then(res => {
          resolve(res)
        }, err => {
          reasons.push(err)
          if (reasons.length === promises.length) {
            reject(new AggregateError(reasons))
          }
        })
      })
    })
  }
}



// const promise = new MyPromise((resolve, reject) => {
//   resolve('111111')
//   // reject('2222222')
// })

const p1 = new MyPromise((resolve, reject) =>{ setTimeout(() => {reject('111111')}, 1000) })
const p2 = new MyPromise((resolve, reject) =>{ setTimeout(() => {reject('22222222')}, 2000) })
const p3 = new MyPromise((resolve, reject) =>{ setTimeout(() => {reject('333333333')}, 3000) })

MyPromise.any([p1, p2, p3]).then(res => {
  console.log('res', res)
}).catch(err => {
  console.log('err', err)
})

// MyPromise.reject('银峰11111').then(res => {
//   console.log(res)
// }).catch(err => {
//   console.log('err', err)
// })


// promise.then(res => {
//   console.log('res', res)
// }, err => {
//   console.log('err', err)
// }).then(res => {
//   console.log('res2', res)
// }, err => {
//   console.log('err2', err)
// })

// promise.then(res => {
//   console.log('res2', res)
// }, err => {
//   console.log('err2', err)
// })

// setTimeout(() => {
//   promise.then(res => {
//     console.log('res3', res)
//   }, err => {
//     console.log('err3', err)
//   })
// }, 1000)


// 需求
// 多个then执行
// settimeout中执行promise
// 多个then链式调用

// catch
// promise.then(res => {
//   console.log('res', res)
// }).catch(err => {
//   console.log('err2', err)
// }).finally(() => {
//   console.log('最后执行')
// })

迭代器和生成器

// 迭代器:可以对对数据结构遍历的对象
// 满足迭代器协议:
//   1. 迭代器协议定义了产生一系列值(无论是有限还是无限个)的标准方式;
//   2. 那么在js中这个标准就是一个特定的next方法

// 迭代器
function createIterator(arr) {
  let index = 0
  return {
    next: function () {
      if (index < arr.length) {
        return { done: false, value: arr[index++] }
      } else {
        return { done: true, value: undefined }
      }
    }
  }
}


// 可迭代对象
// 必须有[Symbol.iterator]属性, 返回一个迭代器对象
const namesIterable = {
  names: ['bmn', 'dsv', 'fgb'],
  [Symbol.iterator]: function () {
    let index = 0
    return {
      next: () => {
        if (index < this.names.length) {
          return { done: false, value: this.names[index++] }
        } else {
          return { done: true, value: undefined }
        }
      }
    }
  }
}

// 生成器
// 1.生成器函数需要在function的后面加一个符号:*   

//2.生成器函数可以通过yield关键字来控制函数的执行流程

//3.生成器函数的返回值是一个Generator(生成器)
function* foo() {
  const res1 = yield 100
  console.log(res1)
  const res2 = yield 200 + res1
  console.log(res2)
  const res3 = yield 300 + res2
  console.log(res3)
  console.log('执行完毕')
}

const generator = foo()
// next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
console.log(generator.next())
console.log(generator.next(1))
console.log(generator.next(2))
console.log(generator.next(0))
/*{ value: 100, done: false }
1
{ value: 201, done: false }
2
{ value: 302, done: false }
0
执行完毕
{ value: undefined, done: true }*/

function* createIterator(arr) {
  for (const item of arr) {
    yield item
  }
}
const generator = createIterator(['vbnf','avc','sfg'])
for (let item of generator) {
  console.log(item)
}

image-20211226141901057 image-20211226141910110

异步终极方案 Async/await

浏览器的事件循环

image-20211126115209114

微任务和宏任务

image-20211126155451126

await后面执行的数据相当于放到微任务队列中

时间循环面试题

// 难度较大
Promise.resolve().then(() => {
  console.log(0);
  // 1.直接return一个值 相当于resolve(4)
  // return 4

  // 2.return thenable的值
  return {
    then: function(resolve) {
      // 大量的计算
      resolve(4)
    }
  }

  // 3.return Promise
  // 不是普通的值, 多加一次微任务
  // Promise.resolve(4), 多加一次微任务
  // 一共多加两次微任务
  return Promise.resolve(4)
}).then((res) => {
  console.log(res)
})

Promise.resolve().then(() => {
  console.log(1);
}).then(() => {
  console.log(2);
}).then(() => {
  console.log(3);
}).then(() => {
  console.log(5);
}).then(() =>{
  console.log(6);
})

// 1.return 4
// 0
// 1
// 4
// 2
// 3
// 5
// 6

// 2.return thenable
// 0
// 1
// 2
// 4
// 3
// 5
// 6

// 3.return promise
// 0
// 1
// 2
// 3
// 4
// 5
// 6

Node中的事件循环

image-20211226165040940

Node中nextTick优先其他微任务执行,setTimeout优先setImmediate执行

require的用法

image-20211228210116190

image-20220105010246203

模块加载的过程

image-20211228213522722

ESModule执行过程

image-20211229125549697

包管理工具

image-20211230141518991

npm install 原理

image-20211230143144285

yarn

image-20211230151331579

cnpm

image-20211230153019571

npm发布

image-20211230161026785

Cookie BOM DOM

image-20211231162416449

参数作用域

image-20220101143650155

7. DOMContentLoaded

7.1 是什么

当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载。

这时问题又来了,“HTML 文档被加载和解析完成”是什么意思呢?或者说,HTML 文档被加载和解析完成之前,浏览器做了哪些事情呢?那我们需要从浏览器渲染原理来谈谈。

浏览器向服务器请求到了 HTML 文档后便开始解析,产物是 DOM(文档对象模型),到这里 HTML 文档就被加载和解析完成了。如果有 CSS 的会根据 CSS 生成 CSSOM(CSS 对象模型),然后再由 DOM 和 CSSOM 合并产生渲染树。有了渲染树,知道了所有节点的样式,下面便根据这些节点以及样式计算它们在浏览器中确切的大小和位置,这就是布局阶段。有了以上这些信息,下面就把节点绘制到浏览器上。所有的过程如下图所示:

图片

现在你可能了解 HTML 文档被加载和解析完成前浏览器大概做了哪些工作,但还没完,因为我们还没有考虑现在前端的主角之一 JavaScript。

JavaScript 可以阻塞 DOM 的生成,也就是说当浏览器在解析 HTML 文档时,如果遇到

当 HTML 文档被解析时如果遇见(同步)脚本,则停止解析,先去加载脚本,然后执行,执行结束后继续解析 HTML 文档。过程如下图:

图片image.png

defer 脚本:

当 HTML 文档被解析时如果遇见 defer 脚本,则在后台加载脚本,文档解析过程不中断,而等文档解析结束之后,defer 脚本执行。另外,defer 脚本的执行顺序与定义时的位置有关。过程如下图:

图片

async 脚本:

当 HTML 文档被解析时如果遇见 async 脚本,则在后台加载脚本,文档解析过程不中断。脚本加载完成后,文档停止解析,脚本执行,执行结束后文档继续解析。过程如下图:

图片image.png

如果你 Google "async 和 defer 的区别",你可能会发现一堆类似上面的文章或图片,而在这里,我想跟你分享的是 async 和 defer 对 DOMContentLoaded 事件触发的影响。

defer 与 DOMContentLoaded

如果 script 标签中包含 defer,那么这一块脚本将不会影响 HTML 文档的解析,而是等到 HTML 解析完成后才会执行。而 DOMContentLoaded 只有在 defer 脚本执行结束后才会被触发。所以这意味着什么呢?HTML 文档解析不受影响,等 DOM 构建完成之后 defer 脚本执行,但脚本执行之前需要等待 CSSOM 构建完成。在 DOM、CSSOM 构建完毕,defer 脚本执行完成之后,DOMContentLoaded 事件触发。

async 与 DOMContentLoaded

如果 script 标签中包含 async,则 HTML 文档构建不受影响,解析完毕后,DOMContentLoaded 触发,而不需要等待 async 脚本执行、样式表加载等等。

7.3 DOMContentLoaded 与 load

在回头看第一张图:

图片

与标记 1 的蓝线平行的还有一条红线,红线就代表 load 事件触发的时间,对应的,在最下面的概览部分,还有一个用红色标记的 "Load:1.60s",描述了 load 事件触发的具体时间。

这两个事件有啥区别呢?点击这个网页你就能明白:testdrive-archive.azu...

解释一下,当 HTML 文档解析完成就会触发 DOMContentLoaded,而所有资源加载完成之后,load 事件才会被触发。

另外需要提一下的是,我们在 jQuery 中经常使用的 (document).ready(function()//...代码...);其实监听的就是DOMContentLoaded事件,而(document).load(function() { // ...代码... }); 监听的是 load 事件。

7.4 使用

document.addEventListener("DOMContentLoaded", function(event) {
      console.log("DOM fully loaded and parsed");
  });

原型链

image-20211126233905118

Object.prototype.toString.call() 判断对象类型

Array篇JS实现

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script>
  const players = [
    {name: '科比', num: 24},
    {name: '詹姆斯', num: 23},
    {name: '保罗', num: 3},
    {name: '威少', num: 0},
    {name: '杜兰特', num: 35}
  ]
  // Array篇
  // foEach
  Array.prototype.t_forEach = function (callback) {
    for (let i = 0; i < this.length; i++) {
      console.log(callback(this[i], i, this) === true);
    }
  }

  players.t_forEach((item, index, arr) => {
    console.log(item, index, arr)
  })

  // map
  Array.prototype.t_map = function (callback) {
    let res = []
    for (let i = 0; i < this.length; i++) {
      res.push(callback(this[i], i, this))
    }
  }

  const t_map = players.t_map((item, index, arr) => {
    console.log(item, index)
  })
  console.log(t_map)

  // filter
  Array.prototype.t_filter = function (callback) {
    let res = []
    for (let i = 0; i < this.length; i++) {
      callback(this[i], i, this) && res.push(this[i])
    }
    return res
  }

  const t_filter = players.t_filter((item, index) => {
    return item.num > 23
  })
  console.log(t_filter)

  // every
  Array.prototype.t_every = function (callback) {
    let flag = true
    for (let i = 0; i < this.length; i++) {
      if (!callback(this[i], i, this)) {
        flag = false
        break
      }
    }
    return flag
  }

  console.log(players.t_every((item, index) => {
    return item.num >= 0
  }));

  // some
  Array.prototype.t_some = function (callback) {
    let flag = false
    for (let i = 0; i < this.length; i++) {
      if (callback(this[i], i, this)) {
        flag = true
        break
      }
    }
    return flag
  }

  console.log(players.t_some((item, index) => {
    return item.num > 30
  }));

  // reduce
  Array.prototype.t_reduce = function (callback, initValue) {
    let pre = initValue
    for (let i = 0; i < this.length; i++) {
      pre = callback(pre, this[i])
    }
    return pre
  }
  console.log(players.t_reduce((pre, next) => {
    return pre + next.num
  }, 0));

  // findIndex
  Array.prototype.t_findIndex = function (callback) {
    for (let i = 0; i < this.length; i++) {
      if (callback(this[i], i, this)) {
        return i
      }
    }
    return -1
  }
  console.log(players.t_findIndex((item, index) => {
    return item.num === 35
  }));

  // find
  Array.prototype.t_find = function (callback) {
    for (let i = 0; i < this.length; i++) {
      if (callback(this[i], i, this)) {
        return this[i]
      }
    }
    return undefined
  }
  console.log(players.t_find((item, index) => {
    return item.num === 100
  }));

  // fill
  Array.prototype.t_fill = function (value, start = 0, end) {
    end = end ?? this.length
    for (let i = start; i < end; i++) {
      this[i] = value
    }
    return this
  }
  console.log(players.t_fill('txc', 1, 3));

  // includes 可以识别NaN
  Array.prototype.t_includes = function (value, start = 0) {
    if (start < 0) start = start + this.length
    const isNaN = Number.isNaN(value)
    for (let i = start; i < this.length; i++) {
      if (this[i] === value || Number.isNaN(this[i]) === isNaN) {
        return true
      }
    }
    return false
  }

  console.log([1, 2, 3].includes(1, 1))
  console.log([2, 3, 4, NaN].includes(NaN))

  // join 将数组用分隔符拼成字符串,分隔符默认为,
  Array.prototype.t_join = function (s = ',') {
    let str = ''
    for (let i = 0; i < this.length; i++) {
      str = i === 0 ? `${this[i]}` : `${str}${s}${this[i]}`
    }
    return str
  }

  let arr = [1, 2, 3]
  console.log(arr.t_join());

  // flat
  Array.prototype.t_flat = function () {
    let arr = this
    while (arr.some(item => Array.isArray(item))) {
      arr = [].concat(...arr)
    }
    return arr
  }
  console.log([1, [2, 3, [4]]].t_flat())
</script>
</body>
</html>

Object原生实现

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script>


  const obj = {
    name: 'txc',
    age: 24,
    hobby: '打乒乓球'
  }
  // entries 将对象转为键值对数组
  Object.t_entries = function (obj) {
    let res = []
    for (let key in obj) {
      res.push([key, obj[key]])
    }
    return res
  }
  console.log(Object.t_entries(obj));

  // fromEntries 将键值对数组转为对象
  Object.t_fromEntries = function (arr) {
    let obj = {}
    for (let i = 0; i < arr.length; i++) {
      let [key, value] =  arr[i]
      obj[key] = value
    }
    return obj
  }
  let arr = [['name', '张三'], ['age', 20]]
  console.log(Object.t_fromEntries(arr));

  // keys
  Object.t_keys = function (obj) {
    let res = []
    for (let key in obj) {
      res.push(key)
    }
    return res
  }
  console.log(Object.t_keys(obj));

  // values
  Object.t_values = function (obj) {
    let res = []
    for (let key in obj) {
      res.push(obj[key])
    }
    return res
  }
  console.log(Object.t_values(obj));

  //instanceof
  function instanceOf(parent, child) {
    const fp = parent.prototype
    let cp = child.__proto__
    while (cp) {
      if (fp === cp) {
        return true
      }
      cp = cp.__proto__
    }
    return false
  }
  function Person(name) {
    this.name = name
  }
  const p1 = new Person('张三')
  console.log(instanceOf(Person, p1))

  // is
  Object.t_is = function (x, y) {
    if (x === y) {
      // 防止 -0 和 +0
      return x !== 0 || 1 / x === 1 / y
    }
    // 防止NaN
    return x !== x && y !== y
  }

  const a = { name: 'txc' }
  const b = a
  const c = { name: 'txc' }

  console.log(Object.t_is(a, b))
  console.log(Object.t_is(a, c))
</script>
</body>
</html>