做题的本质:
拿到面试题,第一反应考点(知识点)是什么,而不是题目本身.所以先到知识点再到题目
考点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 }
考点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__ } }
手写 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) }
手写 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)
手写Object.create
function create(obj) { function Fn() {} Fn.prototype = obj return new Fn() }
考点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);