2js考点和面试题

 

做题的本质:
拿到面试题,第一反应考点(知识点)是什么,而不是题目本身.所以先到知识点再到题目

考点1:js基本数据类型

typeof

 可以识别所有的基本数据类型

可以识别函数类型。

不可识别具体的引用类型

总结:除了Functon外的构造函数的new 出来的实例类型都是'object'。面试时候出了typeof function基本就是function

 typeof null==='object'// JavaScript 诞生以来便如此 null是空指针对象

console.log(typeof 1); //'number'
console.log(typeof 'a'); //'string'
console.log(typeof true); //'boolean'
console.log(typeof undefined); //'undefined'
console.log(typeof {
  a: 12
}); //'object'
console.log(typeof null); //'object'

 console.log(typeof [1, 2]); //'object' 
console.log(typeof new Date()); //'object'

console.log(typeof function () {}); //'function'
console.log(
typeof Math.max); //'function' //除Function外所有构造函数的类型都是'object'

console.log(typeof new String('abc'));
console.log(
typeof new Number(123));
console.log(
typeof new Function()); //‘function’

 null undefined

null表示没有对象,即表示该处不应该有值

用于对象原型链的终点

undefined 表示该处缺少一个值,只不过尚未定义

变量初始化但未赋值,默认值是undefined;函数参数默认undefined;函数默认返回时是undefined;对象不存在的属性,该属性值是undefined

==&=== 

 ==默认有数据类型隐式转换 ===严格等于(类型相等,数值相等)实际开发中建议多使用=== 
 

深拷贝

function deepClone(obj) {
  // 如果不是引用类型(默认就是一些基本数据类型和函数类型和undefined)直接返回
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
  let result
  if (obj instanceof Array) {
    result = []
  } else {
    result = {}
  }
  // for-in循环 array 和object都可以使用 便利key
  for (let key in obj) {
    if (!obj.hasOwnProperty(key)) continue
    // 递归拷贝
    result[key] = deepClone(obj[key]);
  }
  return result
}
View Code

考点2:原型和原型链

 每个实例对象默认都有__proto__隐式属性,每个类默认自带prototype显示属性。实例.__proto__===类.prototype。上图中Student.prototype本身也是一个对象

当前实例首先查找自身属性,
 
如果找不到自动通过__proto__查找原型对象上的属性(原型对象里面基本存实例方法)
 

 手写instanceof

object instanceof constructor

instanceof 运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

 console.log(s instanceof Student);//true

console.log(s instanceof Person);//true
console.log(s instanceof Object);//true
实例对象s。 s.__proto__=Student.prototype
s.__proto__.__proto__=Perosn.prototype
s.__proto__.proto__.__proto__=Object.prototype
function instance_of(leftObject, rightConstructor) {
  let rightProto = rightConstructor.prototype
  let leftValue = leftObject.__proto__
  while (true) {
    if (leftValue === null) return false
    if (leftValue === rightProto) return true

    leftValue = leftValue.__proto__
  }

}
View Code

手写 call apply bind

将函数设为对象属性值,执行对象的函数方法,删除该对象的函数属性和属性值(函数)

// call bind apply 作用手动指定函数中的上下文this fn.call(context) 此时 fn函数中的this===context
const obj = {
  name: 'zs',
  age: 12,
  gender: 'male',
}

function fn(a, b) {
  console.log(this);
  console.log(a, b);
}

Function.prototype.call1 = function (context, ...rest) {
  // call函数中的this是fn
  context = Object(context) || window
  context.fn = this
  context.fn(...rest) //这句话是本质
  delete context.fn

}
fn.call1(obj, 1, 2)


Function.prototype.bind1 = function (context, ...rest) {
  context = Object(context) || window
  context.fn = this
  return () => {
    context.fn(...rest)
  }
}
const fn2 = fn.bind1(obj, 1, 2)
fn2()

function fn1(arr) {
  console.log(arr);
}
Function.prototype.apply1 = function (context, arr) {
  context = Object(context) || window
  context.fn = this
  context.fn(arr)

}
View Code

 手写 new

创建一个空对象,这个空对象的__proto__指向构造函数的prototype 这样保证构造出来实例对象可以访问构造函数原型上实例方法 Object.create

 执行构造函数,给空对象赋属性值 call|apply,这个时候空对象里面就有属性和属性值。

返回这个空对象。

// Object.create(obj)方法创建一个新空对象{},使用现有的对象obj来提供新创建的对象的__proto__。
// 首先创建一个空的对象,空对象的__proto__属性指向构造函数的原型对象
// 把上面创建的空对象赋值构造函数内部的this,用构造函数内部的方法修改空对象
// 如果构造函数返回一个非基本类型的值,则返回这个值,否则上面创建的对象
function _new(Constructor, ...args) {
  const obj = Object.create(Constructor.prototype)
  const res = Constructor.call(obj, ...args) //给空对象赋属性值
  return res instanceof Object ? res : obj
}
const p = _new(Person, 'zs', 10)
View Code

 手写Object.create

function create(obj) {
    function Fn() {}
    Fn.prototype = obj
    return new Fn()

  }
View Code

考点3:js作用域和闭包

es6转es5

https://www.jianshu.com/p/8a8f7b0f887a

 闭包的形式

手写防抖和节流

函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。

函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。

区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
 // 防抖函数
    function debounce(fn, delay) {
      let timer = null //闭包
      return function () {
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
          fn.apply(this, arguments) //频繁操作之后最后一次执行代码 注意:箭头函数里面没有this arguments参数。
          timer = null
        }, delay)
      }
    }


//防抖2 作用一样 写法不一样而已
function debounce1(fn, delay) {
  let timer = null
  return (...args) => {
    timer && clearTimeout(timer)
    timer = setTimeout(() => {
      fn(...args)
    }, delay)

  }
}
 

例子

输入框防抖

    const input = document.querySelector('input');

    function fn(e) {
      const value = e.target.value
      console.log(value);
    }
    // 防抖函数
    function debounce(fn, delay) {
      let timer = null //闭包
      return function () {
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
          fn.apply(this, arguments) //频繁操作之后最后一次执行代码
          timer = null
        }, delay)
      }
    }
    fn = debounce(fn, 500)
    input.addEventListener('input', fn, false);
View Code
原文地址:https://www.cnblogs.com/xiaoliziaaa/p/13790489.html