前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >对JS原理很无奈?送给被原理题打蒙圈的面试者!(一)

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

作者头像
用户1272076
发布2019-07-30 10:13:11
6810
发布2019-07-30 10:13:11
举报
文章被收录于专栏:张培跃

前端面试的套路貌似越来越深了,尤其是当面试官向你抛来各种JS原理性的问题时,蒙蒙的感觉就上来了。所以是时候准备些反套路了…… 注:本文仅提供了相应的核心原理及思路,部分细节未处理。

一、call、apply与bind的实现
  • 由于callapplybind都是属于 Function.prototype对象下的方法,所以每个function实例都拥有有 callapplybind属性。
  • 相同点:都是为改变 this指向而存在的。
  • 异同点:使用call方法时,传递给函数的参数必须逐个列举出来,使用apply方法时,传递给函数的是参数数组。bindcall很相似,第一个参数是 this的指向,从第二个参数开始是接收的参数列表。bind方法不会立即执行,而是返回一个改变了上下文 this后的函数,用于稍后调用。callapply则是立即调用。
1、call实现原理:
代码语言:javascript
复制
Function.prototype.mycall = function (context) {
    // 当context为null时,其值则为window
    context = context || window;
    // this为调用mycall的函数。将this赋值给context的fn属性
    context.fn = this;
    // 将arguments转为数组,并从下标1位置开如截取
    let arg = [...arguments].slice(1);
    // 将arg数组的元素作为fn方法的参数执行,结果赋值给result
    let result = context.fn(...arg);
    // 删除fn属性
    delete context.fn;
    // 返回结果
    return result;
}

测试:

代码语言:javascript
复制
function add(c, d){
    return this.a + this.b + c + d;
}
var obj = {a:1, b:2};
console.log(add.mycall(obj, 3, 4)); // 10

2、apply实现原理

代码语言:javascript
复制
Function.prototype.myapply = function (context) {
    // 当context为null时,其值则为window
    context = context || window
    // this为调用myapply的函数。将this赋值给context的fn属性
    context.fn = this;
    // 如果未传值,则为一空数组
    let arg = arguments[1] || [];
    // 将arg数组的元素作为fn方法的参数执行,结果赋值给result
    let result = context.fn(...arg);
    // 删除fn属性
    delete context.fn
    // 返回结果
    return result
}

测试:

代码语言:javascript
复制
function add(c, d){
    return this.a + this.b + c + d;
}
var obj = {a:1, b:2};
console.log(add.myapply(obj, [5, 6])); // 14
代码语言:javascript
复制
3、bind实现原理
代码语言:javascript
复制
Function.prototype.mybind = function (context) {
    // this为调用mybind的函数。将this赋值给变量_this
    let _this = this;
    // 将arguments转为数组,并从下标1位置开如截取
    let arg = [...arguments].slice(1);
    // 返回函数fn
    return function fn(){
        // 通过apply方法调用函数并返回结果。
        return _this.apply(context, arg.concat(...arguments));
    }
}
代码语言:javascript
复制
测试:
var obj = {
    siteName: "zhangpeiyue.com"
}
function printSiteName() {
    console.log(this.siteName);
}
var site = printSiteName.mybind(obj);
// 返回的是一个函数
console.log(site) // function () { … }
// 通过mybind使其this发生了变化
site();// zhangpeiyue.com

二、浅拷贝与深拷贝
  • 浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用。
  • 深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”。
  • 我们希望在改变新的数组(对象)的时候,不改变原数组(对象)时需要使用深拷贝。
1、浅拷贝实现

原对象

代码语言:javascript
复制
const obj = {
    siteName:"张培跃",
    info:{
        age:18,
        props:["阳光","帅气"]
    }
}
// 1. ...实现
let copy1 = {...obj};
console.log(copy1 === obj);// false
console.log(copy1.info === obj.info);// true
console.log(copy1.siteName);// 张培跃

// 2. Object.assign实现
let copy2 = Object.assign({},obj);
console.log(copy2 === obj);// false
console.log(copy2.info === obj.info);// true
console.log(copy2.siteName);// 张培跃

// 3、for in 实现
let copy3 = {};
for(let key in obj){
    copy3[key] = obj[key];
}
console.log(copy2 === obj);// false
console.log(copy2.info === obj.info);// true
console.log(copy2.siteName);// 张培跃

原数组

代码语言:javascript
复制
const arr =[{
    siteName:"张培跃",
    info:{
        age:18,
        props:["阳光","帅气"]
    }
}]
// 1. ...实现
let copy1 = {...arr};
console.log(copy1 === arr);// false
console.log(copy1[0].info === arr[0].info);// true
console.log(copy1[0].siteName);// 张培跃

// 2. Array.from实现
let copy2 = Array.from(arr);
console.log(copy2 === arr);// false
console.log(copy2[0].info === arr[0].info);// true
console.log(copy2[0].siteName);// 张培跃

// 3、forEach实现
let copy3 = [];
arr.forEach(v=>copy3.push(v));
copy3 = Array.from(arr);
console.log(copy3 === arr);// false
console.log(copy3[0].info === arr[0].info);// true
console.log(copy3[0].siteName);// 张培跃

// 4、map实现
let copy4 = arr.map(v=>v);
console.log(copy4 === arr);// false
console.log(copy4[0].info === arr[0].info);// true
console.log(copy4[0].siteName);// 张培跃

2、深拷贝实现

  • JOSN.stringify()/JSON.parse()
代码语言:javascript
复制
const obj = {
    siteName:"张培跃",
    info:{
        age:18,
        props:["阳光","帅气"]
    }
}
const copy1 = JSON.parse(JSON.stringify(obj));
console.log(copy1 === obj);// false
console.log(copy1.info === obj.info);// false
console.log(copy1.info.props === obj.info.props);// false
console.log(copy1.siteName);// 张培跃
  • 递归拷贝
代码语言:javascript
复制
function deepClone(obj) {
    // 判断是否数组或对象,确定初始值
    let copy = obj instanceof Array ? [] : {}
    for (let i in obj) {
        if (obj.hasOwnProperty(i)) {
            copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
        }
    }
    return copy
}
const obj = {
    siteName:"张培跃",
    info:{
        age:18,
        props:["阳光","帅气"]
    }
}
const copy1 = deepClone(obj);
console.log(copy1 === obj);// false
console.log(copy1.info === obj.info);// false
console.log(copy1.info.props === obj.info.props);// false
console.log(copy1.siteName);// 张培跃
代码语言:javascript
复制

三、setTimeout模拟setInterval
  • setTimeoutsetInterval的语法相同。它们都有两个参数,一个是将要执行的代码字符串,还有一个是以毫秒为单位的时间间隔。
代码语言:javascript
复制
setInterval(()=>{}, 1000);
setTimeout(()=>{}, 1000);
代码语言:javascript
复制
  • 区别: setInterval在执行完一次代码之后,经过指定的时间间隔,执行代码,而 setTimeout只执行一次那段代码。
  • 注意:假设 setTimeout定时器指定时间为1秒,而函数的执行时间是2秒,则 setTimeout的总运行总时长为3秒。而 setInterval不会被调用的函数所束缚,它只是简单地每隔一定时间就重复执行一次指定的函数。所以在函数的逻辑比较复杂,所处理的时间较长时, setInterval有可能会产生连续干扰的问题。若要避免这一问题,建议通过 setTimeout来模拟一个 setInterval

实现:

代码语言:javascript
复制
// 可避免setInterval因执行时间导致的间隔执行时间不一致
setTimeout (function () {
  // do something
  setTimeout (arguments.callee, 500)
}, 500)

四、 new本质

new 构造函数的执行流程:
  • 创建对象,并给予属性名为 __proto__,值为构造函数原型( prototype)的属性。
  • 将构造函数的 this指向为刚创建的对象。
  • 执行构造函数的语句。
  • 将创建的对象进行返回。
代码语言:javascript
复制
function myNew (fun) {
    return function () {
        // 创建一个新对象且将其隐式原型指向构造函数原型
        let obj = {
            __proto__ : fun.prototype
        }
        // 执行构造函数
        fun.call(obj, ...arguments);
        // 返回该对象
        return obj;
    }
}

function Site(siteName, siteUrl) {
    this.siteName = siteName;
    this.siteUrl = siteUrl;
}
let obj = myNew(Site)("张培跃","http://www.zhangpeiyue.com");
console.log(obj);// { siteName: '张培跃', siteUrl: 'http://www.zhangpeiyue.com' }

五、 instanceof的原理

instanceof 来判断对象的具体类型,当然, instanceof 也可以判断一个实例是否是其父类型或者祖先类型的实例。先来看几个示例:

代码语言:javascript
复制
function Box(){}

function Desk(){}

var desk1 = new Desk();
console.log(desk1 instanceof Desk);// true
console.log(desk1 instanceof Object);// true

Desk.prototype = Box.prototype;
console.log(desk1 instanceof Desk);// false
console.log(desk1 instanceof Box);// false
console.log(desk1 instanceof Object);// true;

var  desk2= new Desk();
console.log(desk2 instanceof Desk);// true
console.log(desk2 instanceof Box);// true
console.log(desk2 instanceof Object);// true

Desk.prototype = null;
var desk3 = new Desk();
console.log(desk3 instanceof Box);// false
console.log(desk3 instanceof Object);// true
console.log(desk3 instanceof Desk); // error
代码语言:javascript
复制
  • instanceof的原理是:右边变量的原型是否存在于左边变量的原型链上。如果在,返回 true,如果不在则返回false。不过有一个特殊的情况,当右边的 prototypenull将会报错(类似于空指针异常)。
  • 实现:
代码语言:javascript
复制
function instanceOf(left, right) {
    let leftProto = left.__proto__
    let rightPrototype = right.prototype
    if(rightPrototype ===  null){
        throw new TypeError('Function has non-object prototype null in instanceof check');
    }
    while (true) {
        if (leftProto === null)
            return false;
        if (leftProto === rightPrototype)
            return true;
        leftProto = leftProto.__proto__;
    }
}
  • 测试:
代码语言:javascript
复制
function Box(){}

function Desk(){}

var desk1 = new Desk();
console.log(instanceOf(desk1,Desk));// true
console.log(instanceOf(desk1,Object));// true

Desk.prototype = Box.prototype;
console.log(instanceOf(desk1,Desk));// false
console.log(instanceOf(desk1,Box));// false
console.log(instanceOf(desk1,Object));// true;

var  desk2= new Desk();
console.log(instanceOf(desk2,Desk));// true
console.log(instanceOf(desk2,Box));// true
console.log(instanceOf(desk2,Object));// true

Desk.prototype = null;
var desk3 = new Desk();
console.log(instanceOf(desk3,Box));// false
console.log(instanceOf(desk3,Object));// true
console.log(instanceOf(desk3,Desk));// error

未完,待续!

—————END—————

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 张培跃 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、call、apply与bind的实现
    • 1、call实现原理:
      • 3、bind实现原理
      • 二、浅拷贝与深拷贝
        • 1、浅拷贝实现
        • 三、setTimeout模拟setInterval
          • new 构造函数的执行流程:
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档