对JS原理很无奈?送给被原理题打蒙圈的面试者!(一)

论坛 期权论坛 期权     
张培跃   2019-7-29 01:09   3350   0
前端面试的套路貌似越来越深了,尤其是当面试官向你抛来各种JS原理性的问题时,蒙蒙的感觉就上来了。所以是时候准备些反套路了……
注:本文仅提供了相应的核心原理及思路,部分细节未处理。
[h3]一、call、apply与bind的实现[/h3]
  • 由于
    1. call
    复制代码
    1. apply
    复制代码
    1. bind
    复制代码
    都是属于
    1. Function.prototype
    复制代码
    对象下的方法,所以每个
    1. function
    复制代码
    实例都拥有有
    1. call
    复制代码
    1. apply
    复制代码
    1. bind
    复制代码
    属性。
  • 相同点:都是为改变
    1. this
    复制代码
    指向而存在的。
  • 异同点:使用
    1. call
    复制代码
    方法时,传递给函数的参数必须逐个列举出来,使用
    1. apply
    复制代码
    方法时,传递给函数的是参数数组。
    1. bind
    复制代码
    1. call
    复制代码
    很相似,第一个参数是
    1. this
    复制代码
    的指向,从第二个参数开始是接收的参数列表。
    1. bind
    复制代码
    方法不会立即执行,而是返回一个改变了上下文
    1. this
    复制代码
    后的函数,用于稍后调用。
    1. call
    复制代码
    1. apply
    复制代码
    则是立即调用。
1、call实现原理:
  1. Function.prototype.mycall = function (context) {
复制代码
  1.     // 当context为null时,其值则为window
复制代码
  1.     context = context || window;
复制代码
  1.     // this为调用mycall的函数。将this赋值给context的fn属性
复制代码
  1.     context.fn = this;
复制代码
  1.     // 将arguments转为数组,并从下标1位置开如截取
复制代码
  1.     let arg = [...arguments].slice(1);
复制代码
  1.     // 将arg数组的元素作为fn方法的参数执行,结果赋值给result
复制代码
  1.     let result = context.fn(...arg);
复制代码
  1.     // 删除fn属性
复制代码
  1.     delete context.fn;
复制代码
  1.     // 返回结果
复制代码
  1.     return result;
复制代码
  1. }
复制代码
测试:
  1. function add(c, d){
复制代码
  1.     return this.a + this.b + c + d;
复制代码
  1. }
复制代码
  1. var obj = {a:1, b:2};
复制代码
  1. console.log(add.mycall(obj, 3, 4)); // 10
复制代码
2、apply实现原理
  1. Function.prototype.myapply = function (context) {
复制代码
  1.     // 当context为null时,其值则为window
复制代码
  1.     context = context || window
复制代码
  1.     // this为调用myapply的函数。将this赋值给context的fn属性
复制代码
  1.     context.fn = this;
复制代码
  1.     // 如果未传值,则为一空数组
复制代码
  1.     let arg = arguments[1] || [];
复制代码
  1.     // 将arg数组的元素作为fn方法的参数执行,结果赋值给result
复制代码
  1.     let result = context.fn(...arg);
复制代码
  1.     // 删除fn属性
复制代码
  1.     delete context.fn
复制代码
  1.     // 返回结果
复制代码
  1.     return result
复制代码
  1. }
复制代码
测试:
  1. function add(c, d){
复制代码
  1.     return this.a + this.b + c + d;
复制代码
  1. }
复制代码
  1. var obj = {a:1, b:2};
复制代码
  1. console.log(add.myapply(obj, [5, 6])); // 14
复制代码
    1. [/code]
    2. [/list]3、bind实现原理[list][*][*][*][*][*][*][*][*][*][*][*][/list][code]Function.prototype.mybind = function (context) {
    复制代码
    1.     // this为调用mybind的函数。将this赋值给变量_this
    复制代码
    1.     let _this = this;
    复制代码
    1.     // 将arguments转为数组,并从下标1位置开如截取
    复制代码
    1.     let arg = [...arguments].slice(1);
    复制代码
    1.     // 返回函数fn
    复制代码
    1.     return function fn(){
    复制代码
    1.         // 通过apply方法调用函数并返回结果。
    复制代码
    1.         return _this.apply(context, arg.concat(...arguments));
    复制代码
    1.     }
    复制代码
    1. }
    复制代码
    测试:
    1. var obj = {
    复制代码
    1.     siteName: "zhangpeiyue.com"
    复制代码
    1. }
    复制代码
    1. function printSiteName() {
    复制代码
    1.     console.log(this.siteName);
    复制代码
    1. }
    复制代码
    1. var site = printSiteName.mybind(obj);
    复制代码
    1. // 返回的是一个函数
    复制代码
    1. console.log(site) // function () { … }
    复制代码
    1. // 通过mybind使其this发生了变化
    复制代码
    1. site();// zhangpeiyue.com
    复制代码
    [h3]二、浅拷贝与深拷贝[/h3]
    • 浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用。
    • 深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”。
    • 我们希望在改变新的数组(对象)的时候,不改变原数组(对象)时需要使用深拷贝。
    1、浅拷贝实现原对象
    1. const obj = {
    复制代码
    1.     siteName:"张培跃",
    复制代码
    1.     info:{
    复制代码
    1.         age:18,
    复制代码
    1.         props:["阳光","帅气"]
    复制代码
    1.     }
    复制代码
    1. }
    复制代码
    1. // 1. ...实现
    复制代码
    1. let copy1 = {...obj};
    复制代码
    1. console.log(copy1 === obj);// false
    复制代码
    1. console.log(copy1.info === obj.info);// true
    复制代码
    1. console.log(copy1.siteName);// 张培跃
    复制代码
    1. [/code][code]// 2. Object.assign实现
    复制代码
    1. let copy2 = Object.assign({},obj);
    复制代码
    1. console.log(copy2 === obj);// false
    复制代码
    1. console.log(copy2.info === obj.info);// true
    复制代码
    1. console.log(copy2.siteName);// 张培跃
    复制代码
    1. [/code][code]// 3、for in 实现
    复制代码
    1. let copy3 = {};
    复制代码
    1. for(let key in obj){
    复制代码
    1.     copy3[key] = obj[key];
    复制代码
    1. }
    复制代码
    1. console.log(copy2 === obj);// false
    复制代码
    1. console.log(copy2.info === obj.info);// true
    复制代码
    1. console.log(copy2.siteName);// 张培跃
    复制代码
    原数组
    1. const arr =[{
    复制代码
    1.     siteName:"张培跃",
    复制代码
    1.     info:{
    复制代码
    1.         age:18,
    复制代码
    1.         props:["阳光","帅气"]
    复制代码
    1.     }
    复制代码
    1. }]
    复制代码
    1. // 1. ...实现
    复制代码
    1. let copy1 = {...arr};
    复制代码
    1. console.log(copy1 === arr);// false
    复制代码
    1. console.log(copy1[0].info === arr[0].info);// true
    复制代码
    1. console.log(copy1[0].siteName);// 张培跃
    复制代码
    1. [/code][code]// 2. Array.from实现
    复制代码
    1. let copy2 = Array.from(arr);
    复制代码
    1. console.log(copy2 === arr);// false
    复制代码
    1. console.log(copy2[0].info === arr[0].info);// true
    复制代码
    1. console.log(copy2[0].siteName);// 张培跃
    复制代码
    1. [/code][code]// 3、forEach实现
    复制代码
    1. let copy3 = [];
    复制代码
    1. arr.forEach(v=>copy3.push(v));
    复制代码
    1. copy3 = Array.from(arr);
    复制代码
    1. console.log(copy3 === arr);// false
    复制代码
    1. console.log(copy3[0].info === arr[0].info);// true
    复制代码
    1. console.log(copy3[0].siteName);// 张培跃
    复制代码
    1. [/code][code]// 4、map实现
    复制代码
    1. let copy4 = arr.map(v=>v);
    复制代码
    1. console.log(copy4 === arr);// false
    复制代码
    1. console.log(copy4[0].info === arr[0].info);// true
    复制代码
    1. console.log(copy4[0].siteName);// 张培跃
    复制代码
    2、深拷贝实现
    • JOSN.stringify()/JSON.parse()
    1. const obj = {
    复制代码
    1.     siteName:"张培跃",
    复制代码
    1.     info:{
    复制代码
    1.         age:18,
    复制代码
    1.         props:["阳光","帅气"]
    复制代码
    1.     }
    复制代码
    1. }
    复制代码
    1. const copy1 = JSON.parse(JSON.stringify(obj));
    复制代码
    1. console.log(copy1 === obj);// false
    复制代码
    1. console.log(copy1.info === obj.info);// false
    复制代码
    1. console.log(copy1.info.props === obj.info.props);// false
    复制代码
    1. console.log(copy1.siteName);// 张培跃
    复制代码
    • 递归拷贝
    1. function deepClone(obj) {
    复制代码
    1.     // 判断是否数组或对象,确定初始值
    复制代码
    1.     let copy = obj instanceof Array ? [] : {}
    复制代码
    1.     for (let i in obj) {
    复制代码
    1.         if (obj.hasOwnProperty(i)) {
    复制代码
    1.             copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
    复制代码
    1.         }
    复制代码
    1.     }
    复制代码
    1.     return copy
    复制代码
    1. }
    复制代码
    1. const obj = {
    复制代码
    1.     siteName:"张培跃",
    复制代码
    1.     info:{
    复制代码
    1.         age:18,
    复制代码
    1.         props:["阳光","帅气"]
    复制代码
    1.     }
    复制代码
    1. }
    复制代码
    1. const copy1 = deepClone(obj);
    复制代码
    1. console.log(copy1 === obj);// false
    复制代码
    1. console.log(copy1.info === obj.info);// false
    复制代码
    1. console.log(copy1.info.props === obj.info.props);// false
    复制代码
    1. console.log(copy1.siteName);// 张培跃
    复制代码
      1. [/code]
      2. [/list][h3]三、setTimeout模拟setInterval[/h3][list][*][code]setTimeout
      复制代码
      1. setInterval
      复制代码
      的语法相同。它们都有两个参数,一个是将要执行的代码字符串,还有一个是以毫秒为单位的时间间隔。
    1. setInterval(()=>{}, 1000);
    复制代码
    1. setTimeout(()=>{}, 1000);
    复制代码
      1. [/code]
      2. [/list][list][*]区别: [code]setInterval
      复制代码
      在执行完一次代码之后,经过指定的时间间隔,执行代码,而
      1. setTimeout
      复制代码
      只执行一次那段代码。
    • 注意:假设
      1. setTimeout
      复制代码
      定时器指定时间为1秒,而函数的执行时间是2秒,则
      1. setTimeout
      复制代码
      的总运行总时长为3秒。而
      1. setInterval
      复制代码
      不会被调用的函数所束缚,它只是简单地每隔一定时间就重复执行一次指定的函数。所以在函数的逻辑比较复杂,所处理的时间较长时,
      1. setInterval
      复制代码
      有可能会产生连续干扰的问题。若要避免这一问题,建议通过
      1. setTimeout
      复制代码
      来模拟一个
      1. setInterval
      复制代码

    实现:
    1. // 可避免setInterval因执行时间导致的间隔执行时间不一致
    复制代码
    1. setTimeout (function () {
    复制代码
    1.   // do something
    复制代码
    1.   setTimeout (arguments.callee, 500)
    复制代码
    1. }, 500)
    复制代码
    四、 new本质
    new 构造函数的执行流程:
    • 创建对象,并给予属性名为
      1. __proto__
      复制代码
      ,值为构造函数原型(
      1. prototype
      复制代码
      )的属性。
    • 将构造函数的
      1. this
      复制代码
      指向为刚创建的对象。
    • 执行构造函数的语句。
    • 将创建的对象进行返回。
    1. function myNew (fun) {
    复制代码
    1.     return function () {
    复制代码
    1.         // 创建一个新对象且将其隐式原型指向构造函数原型
    复制代码
    1.         let obj = {
    复制代码
    1.             __proto__ : fun.prototype
    复制代码
    1.         }
    复制代码
    1.         // 执行构造函数
    复制代码
    1.         fun.call(obj, ...arguments);
    复制代码
    1.         // 返回该对象
    复制代码
    1.         return obj;
    复制代码
    1.     }
    复制代码
    1. }
    复制代码
    1. [/code][code]function Site(siteName, siteUrl) {
    复制代码
    1.     this.siteName = siteName;
    复制代码
    1.     this.siteUrl = siteUrl;
    复制代码
    1. }
    复制代码
    1. let obj = myNew(Site)("张培跃","http://www.zhangpeiyue.com");
    复制代码
    1. console.log(obj);// { siteName: '张培跃', siteUrl: 'http://www.zhangpeiyue.com' }
    复制代码
    五、 instanceof的原理
    1. instanceof
    复制代码
    来判断对象的具体类型,当然,
    1. instanceof
    复制代码
    也可以判断一个实例是否是其父类型或者祖先类型的实例。先来看几个示例:
    1. function Box(){}
    复制代码
    1. [/code][code]function Desk(){}
    复制代码
    1. [/code][code]var desk1 = new Desk();
    复制代码
    1. console.log(desk1 instanceof Desk);// true
    复制代码
    1. console.log(desk1 instanceof Object);// true
    复制代码
    1. [/code][code]Desk.prototype = Box.prototype;
    复制代码
    1. console.log(desk1 instanceof Desk);// false
    复制代码
    1. console.log(desk1 instanceof Box);// false
    复制代码
    1. console.log(desk1 instanceof Object);// true;
    复制代码
    1. [/code][code]var  desk2= new Desk();
    复制代码
    1. console.log(desk2 instanceof Desk);// true
    复制代码
    1. console.log(desk2 instanceof Box);// true
    复制代码
    1. console.log(desk2 instanceof Object);// true
    复制代码
    1. [/code][code]Desk.prototype = null;
    复制代码
    1. var desk3 = new Desk();
    复制代码
    1. console.log(desk3 instanceof Box);// false
    复制代码
    1. console.log(desk3 instanceof Object);// true
    复制代码
    1. console.log(desk3 instanceof Desk); // error
    复制代码
      1. [/code]
      2. [/list][list][*][code]instanceof
      复制代码
      的原理是:右边变量的原型是否存在于左边变量的原型链上。如果在,返回
      1. true
      复制代码
      ,如果不在则返回false。不过有一个特殊的情况,当右边的
      1. prototype
      复制代码
      1. null
      复制代码
      将会报错(类似于空指针异常)。
    • 实现:
    1. function instanceOf(left, right) {
    复制代码
    1.     let leftProto = left.__proto__
    复制代码
    1.     let rightPrototype = right.prototype
    复制代码
    1.     if(rightPrototype ===  null){
    复制代码
    1.         throw new TypeError('Function has non-object prototype null in instanceof check');
    复制代码
    1.     }
    复制代码
    1.     while (true) {
    复制代码
    1.         if (leftProto === null)
    复制代码
    1.             return false;
    复制代码
    1.         if (leftProto === rightPrototype)
    复制代码
    1.             return true;
    复制代码
    1.         leftProto = leftProto.__proto__;
    复制代码
    1.     }
    复制代码
    1. }
    复制代码
    • 测试:
    1. function Box(){}
    复制代码
    1. [/code][code]function Desk(){}
    复制代码
    1. [/code][code]var desk1 = new Desk();
    复制代码
    1. console.log(instanceOf(desk1,Desk));// true
    复制代码
    1. console.log(instanceOf(desk1,Object));// true
    复制代码
    1. [/code][code]Desk.prototype = Box.prototype;
    复制代码
    1. console.log(instanceOf(desk1,Desk));// false
    复制代码
    1. console.log(instanceOf(desk1,Box));// false
    复制代码
    1. console.log(instanceOf(desk1,Object));// true;
    复制代码
    1. [/code][code]var  desk2= new Desk();
    复制代码
    1. console.log(instanceOf(desk2,Desk));// true
    复制代码
    1. console.log(instanceOf(desk2,Box));// true
    复制代码
    1. console.log(instanceOf(desk2,Object));// true
    复制代码
    1. [/code][code]Desk.prototype = null;
    复制代码
    1. var desk3 = new Desk();
    复制代码
    1. console.log(instanceOf(desk3,Box));// false
    复制代码
    1. console.log(instanceOf(desk3,Object));// true
    复制代码
    1. console.log(instanceOf(desk3,Desk));// error
    复制代码
    未完,待续!
    —————END—————
    喜欢本文的朋友们,欢迎长按下图关注公众号
    张培跃,收看更多精彩内容
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:
帖子:
精华:
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP