Fork me on GitHub

m-核心考点

考点+面试题

一、

三、面向对象,构造函数、原型与原型链

继承

  • 1. 对象
    定义:无序属性的集合,其属性可以包含基本值,对象或者函数。
    创建对象的方法:

    • new 方法
    1
    var obj =new Object()
    • 字面量
    1
    var obj = {}
    • 根据原型创建对象
    1
    2
    let obj=Object.create(Object.prototype)
    let obj=Object.create(null)

create 函数的实现

1
2
3
4
5
const create=(proto)=>{
let fun=function(){}
fun.prototype=proto;
return new fun()
}
  • 工厂模式
    就是提供一个生成一类对象的函数,通过这个函数返回一个对象。
    解决代码重复代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var createPerson = function (name, age) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.getName = function () {
    return this.name
    }
    return o;
    }
    var perTom = createPerson('Tom', 20);
    var perJake = createPerson('Jake', 22);

    缺点:

  • 无法识别对象实例

  • 相同的对象的方法会复制很多份,造成浪费

  • 2. 构造函数
    目的:创建一个自定义类。
    使用 new 关键字调用

    1
    2
    3
    4
    5
    function demo() {
    console.log(this)
    }
    demo() //window
    new demo() //demo

    new 方法实现,就是一个高阶函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    var Person = function (name, age) {
    this.name = name;
    this.age = age;
    this.getName = function () {
    return this.name
    }
    }
    //以参数形式传入构造函数
    function New(func) {
    //声明一个中间对象,为最终返回的实例
    var res = {};
    if (func.prototype !== null) {
    //将实例的原型指向构造函数的原型
    res.__proto__ = func.prototype;
    }
    //通过apply将构造函数内部的this指向res,即实例对象
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
    console.log({
    ret,
    res
    })
    //当我们在构造函数中明确指定了返回对象时,那new的执行结果就是该返回对象
    if ((typeof ret === 'object' || typeof ret === 'function') && ret !== null) {
    return ret
    }
    //如果没有明确指定返回对象,则默认返回res,
    return res
    }
    var p1 = New(Person, 'tom', 20);
    console.log(p1.getName()); //tom
    // 判断实例的类型
    console.log(p1 instanceof Person); // true

    new 关键字执行过程:

    1. 声明一个中间对象。
    2. 将该中间对象的原型指向构造函数的原型
    3. 将构造函数的 this,指向中间对象
    4. 如果构造函数存在返回值,返回该返回值,否则返回中间对象
  • 3. 原型
    谈谈你对 JS 原型和原型链的理解:
    原型对象为其他对象提供共享的方法与属性的对象。在创建对象的时,每个对象都包含一个隐式引用指向它的原型对象或者null
    原型也是对象,它也有自己的原型,这个就构成了原型链。

    每个函数都可以是构造函数,每个对象都可以是原型对象。函数 prototype 指向原型,实例__proto__指向原型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //构造函数
    function Person(name, age) {
    this.name = name;
    this.age = age;
    }
    //通过prototype属性,将方法挂载带原型对象上
    Person.prototype.getName= function(){
    return this.name
    }
    var p1 = new Person('tim', 10);
    var p2 = new Person('jak', 22);
    console.log(p1.getName === p2.getName) //true

    原型
    当我们访问实例对象中的属性或方法时,会优先访问实例对象自身的属性和方法。
    判断对象是否拥有某个属性/放发,无论该属性、方法存在于实例对象还是原型对象。

    1
    'name' in p1  //true

    补充:in 的这种特性最常用的场景之一,就是判断当前页面是否在移动端打开。

    1
    let isMoblie = 'ontouchstart' in document

    更简单的原型写法:

    1
    2
    3
    4
    5
    6
    Person.prototype = {
    constructor:Person, // 显示constructor的指向
    getName:function(){},
    getAge:function(){},
    sayHello:function(){}
    }
  • 4. 原型链
    原型链
    add 是 Function 对象的实例。而 Function 的原型对象时 Object 的实例。这构成了一条原型链。

    原型链有什么作用
    存储多个对象的共有属性和方法。我们在访问对象的属性时,实际是查找的整个原型链上的属性,返回第一个找到的对应属性值,过程:首先查找当前对象上是否包含该属性,如果包含就返回返回,否则查找当前对象的原型是否包含,。。。,如果都不包含返回undefined

    实现一个在原型链上查找属性的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const lookupProperty = (object, propertyName) => {
let current = object;

if (current === null) {
throw new Error(`Cannot read property '${propertyName}' of ${object}`)
}
while (current) {
console.log(current)
if (current.hasOwnProperty(propertyName)) {
return current[propertyName]
}
current = Object.getPrototypeOf(current)

}
return undefined

}

console.log(lookupProperty({}, 'toString') === Object.prototype.toString) //true
  • 5. 继承
    如何实现继承

    • 显示原型继承:开发者操作的继承
    1. Object.setPropertyOf(obj1,obj2)
      obj2设置为obj1的原型
    2. Object.create(obj)
      obj为原型创建新对象
    • 隐式原型继承
      使用new关键字实例化时,会自动继承constructorprototype对象,作为实例的原型。
      内置构造函数,如:Object, Array, Boolean, String, Number,Function。

    如何实现超类的继承(Class 的继承)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    const inherit=(SuperConstructor,properties)=>{

    let {constructor}=properties;
    let SubConstructor=function(...args){
    SuperConstructor.call(this,...args);
    constructor.call(this,...args);
    }

    SubConstructor.prototype={
    ...properties,
    constructor:SubConstructor
    }

    Object.setPrototypeOf(
    SubConstructor.prototype,
    SuperConstructor.prototype
    )
    return SuperConstructor
    }

    第一步,编写新的constructor,将两个constructor通过apply/call的方式调用,合并他们的属性,超类优先调用
    第二步,为新的构造函数的原型对象设置为子类的原型,并且修改constructor属性
    第三步,设置新的原型对象继承超类的原型对象。
    原型中比较少人知道的特性
    对象访问__proto__属性,其实就是调用Object.prototype里面的访问时属性get

1
2
3
let obj=new Object();
obj.a=1;
obj.b=2

语法糖-对象字面量
let obj={a:1,b:2}

通过字面量创的新对象,有两层隐式行为

  1. 通过new Object()new Array()等内置构造函数创建新对象
  2. 隐式进行原型继承
1
2
3
4
5
6
7
8
9
10
//构造函数的继承
function cPerson(name, age, job) {
Person.call(this, name, age);
this.job = job
}
//原型链继承
cPerson.prototype = new Person();
//添加方法
cPerson.prototype.getLive = function () {}
var p3 = new cPerson('111', 333, 'ddd')
  • 6. 更好的继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    function Student(name, age, grade) {
    //构造函数继承
    Person.call(this, name, age);
    this.grade = grade;
    }

    function create(proto, options) {
    //创建一个空对象
    var tmp = {};
    //让新的空对象成为父类对象的实例
    tmp.__proto__ = proto;
    //传入的方法都挂载到新对象上,新的对象作为子类对象的原型
    Object.defineProperties(tmp, options);
    return tmp

    }
    //原型继承
    Student.prototype = create(Person.prototype, {
    //重新指定构造函数
    constructor: Student,
    getGrade: {
    value: function () {
    return this.grade
    }
    }
    })

    在 ECMAScript5 中直接提供了一个 Object.create 方法来完成我们上面自己封装的 create 的功能。因此我们可以直接使用 Object.create.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //原型继承
    Student.prototype = Object.create(Person.prototype, {
    //重新指定构造函数
    constructor: {
    value: Student,
    enumerable: false,
    writable: true,
    configurable: true
    },
    getGrade: {
    value: function () {
    return this.grade
    }
    }
    })

    更多继承方式

  • 7. 对象属性
    数据属性包括:

    • value
    • writable
    • configurable
    • enumerable

    访问器属性包括:

    • configurable
    • enumerable
    • get
    • set

    一个属性只能设为访问器属性或数据属性,所以不能同时设置 value、writable 和 get、set

    1
    2
    3
    4
    5
    6
    7
    8
    let a = {
    name: 'ddd'
    }
    console.log(Object.getOwnPropertyDescriptor(a, 'name'))
    Object.defineProperty(a, 'age', {
    value: 13
    })
    console.log(Object.getOwnPropertyDescriptor(a, 'age'))

    对象属性
    注意,直接使用点(.)操作符给对象设置属性,configurable,enumerable,writable 都为 true。
    Object.defineProperties(obj,{}),这些属性默认为 false

    • configurable:表示该属性是否能被 delete 删除。
      当值为 false 时,不能删除,其他特性也不能改变。
    • enumerable:是否能枚举。能否被 for-in 遍历,Object.keys(),。
    • writable:是否能修改值,
    • value:属性值,默认 undefined
    • get:对象访问属性时,get 被调用。
    • set:设置属性值的时候调用。
      使用 Object.getOwnPropertyDescriptor 方法读取某一个属性的特性值。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      let student = {
      name: 'ddd'
      }
      var initValue = 11;
      Object.defineProperty(student, 'age', {
      get: function () {
      console.log('getter');
      return initValue
      },
      set: function (value) {
      console.log('setter');
      initValue = value
      }
      })
      console.log(student.age) //getter 11
      student.age = 12 //setter
      console.log(student.age) //getter 12

四、事件循环

JS 单线程,只有一个事件循环。
函数调用栈-函数执行
任务队列(task queue)-执行除函数外的代码,先进先出(FIFO),可以有多个。

  • 宏任务(macro-task) 、task
    任务源:script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI rendering
  • 微任务(micro-task)、jobs
    任务源:process.nextTick、Promise、Object.observe(已废弃)、MutationObserver(html5 新特性)
1
2
3
4
//setTimeout中的回调函数才是进入任务队列的任务
setTimeout(() => {
console.log('xxx')
}, 10);

setTimeout 作为一个任务分发器,这个函数会立即执行,而它所要分发的任务,也就是它的第一个参数,才是延迟执行。
事件循环的顺序:

  • 从 script 开始第一次循环
  • 全局上下文进入函数调用栈
  • 调用函数,创建函数执行上下文,执行完成,出栈
  • 调用栈清空(只剩全局),就执行 micro-task
  • micro-task 执行完后,再次执行 macro-task
    …….
    setTime 队列
    promise 任务队列

setTime Promise Async Ajax 请求
1. 同步 vs 异步

  • 同步 顺序执行
  • 异步 并行处理方式,不必等待一个程序执行完成,就可以执行其他任务

六、DOM 操作与 BOM 操作

  • 1. DOM 属性和操作
    DOM 操作

  • 2. DOM 事件
    DOM 事件

  • 4.事件代理(事件委托)
    由于事件会在冒泡阶段向上传输到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多子元素的事件。
    优点:

    • 使代码简洁
    • 减少浏览器的内存占用
    • 删除子元素的时候不用考虑删除绑定事件
      应用:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <ul id='list'>
    <li data-index='1'>item1</li>
    <li data-index='2'>item2</li>
    <li data-index='3'>item3</li>
    <li data-index='4'>item4</li>
    </ul>

    let list = document.getElementById('list');
    list.addEventListener('click', e => {
    var event = e || window.event; //兼容
    var target = event.target || event.srcElement;
    //判断是否匹配目标元素
    if (target.nodeName.toLocaleLowerCase() === 'li') {
    console.log(target.dataset.index)
    }

    })
  • 5. BOM 操作
    浏览器对象模型,是浏览器本身的一些信息的设置和获取。

    • window.screen 对象:屏幕的信息
      屏幕宽高

      1
      2
      screen.width
      screen.height
    • window.location 对象:获取当前页面的地址(url),把浏览器重定向到新的页面

      1
      2
      3
      location.href //网址https://juejin.im/timeline/frontend?a=10&b=10#some
      location.protocol //协议https
      location.pathname //路径//juejin.im/timeline/frontend- window.history 对象:浏览器的前进后退等
    1
    2
    history.back() //后退
    history.forward() //前进
    • window.navigator 对象:获取浏览器信息,是否是移动端
    1
    2
    3
    var ua = navigator.userAgent
    var isChrome = ua.indexOf('Chrome') > -1 ? true : false;
    console.log(isChrome)

    [window 对象](https://dorisfeng.github.io/2018/12/21/window%E5%AF%B9%
    location.search //参数?a=10&b=10
    location.hash //#some

    1
    2

    E8%B1%A1/)

八、对象拷贝

对象操作方法

九、手写callapplybind函数

手写

十、

-------------本文结束感谢阅读-------------