原生js面试总结

这里写自定义目录标题- Function.prototype.call()- call的模拟实现- Function.prototype.bind()- Object.prototype.constructor- new 的原理是什么?通过 new 的方式创建对象和通过字面量创建有什么区别?- 原型链- prototype 和 __proto__ 区别- 使用 ES5 实现一个继承 todo- 防...
摘要由CSDN通过智能技术生成

- Function.prototype.call()

  • ES5通过call实现class

在一个对象的上下文中应用另一个对象的方法;参数能够以列表形式传入。

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);
// expected output: "cheese"

- call的模拟实现

Function.prototype.call = function (context) {
    /** 如果第一个参数传入的是 null 或者是 undefined, 那么指向 this 指向 window/global */
    /** 如果第一个参数传入的不是 null 或者是 undefined, 那么必须是一个对象 */
    if (!context) {
        //context 为 null 或者是 undefined
        context = typeof window === 'undefined' ? global : window;
    }
    context.fn = this; //this 指向的是当前的函数 (Function 的实例)
    let args = [...arguments].slice(1);// 获取除了 this 指向对象以外的参数, 空数组 slice 后返回的仍然是空数组
    let result = context.fn(...args); // 隐式绑定, 当前函数的 this 指向了 context.
    delete context.fn;
    return result;
}

// 测试代码
var foo = {
    name: 'Selina'
}
var name = 'Chirs';
function bar(job, age) {
    console.log(this.name);
    console.log(job, age);
}
bar.call(foo, 'programmer', 20);

- Function.prototype.bind()

bind()方法会创建一个新函数,称为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind()方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.


- Object.prototype.constructor

所有对象都会从它的原型上继承一个 constructor 属性:

var o = {};
o.constructor === Object; // true

var o = new Object;
o.constructor === Object; // true

var a = [];
a.constructor === Array; // true

var a = new Array;
a.constructor === Array // true

var n = new Number(3);
n.constructor === Number; // true

- new 的原理是什么?通过 new 的方式创建对象和通过字面量创建有什么区别?

  1. 创建一个新对象;
  2. 这个新对象会被执行 [[原型]] 连接;
  3. 将构造函数的作用域赋值给新对象,即 this 指向这个新对象
  4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
function new(func) {
    let target = {};
    target.__proto__ = func.prototype;
    let res = func.call(target);
    console.log('res: ' +res)
        console.log('typeofres: ' +typeof(res))
    if (typeof(res) == "object" || typeof(res) == "function") {
        return res;
    }
    console.log('tartget: ' + target)
    return target;
}

字面量创建对象,不会调用 Object 构造函数, 简洁且性能更好。

new Object() 方式创建对象本质上是方法调用,涉及到在 proto 链中遍历该方法,当找到该方法后,又会生产方法调用必须的 堆栈信息,方法调用结束后,还要释放该堆栈,性能不如字面量的方式。

通过对象字面量定义对象时,不会调用 Object 构造函数。

- 原型链

class Parent {}
var p = new Parent()
p.constructor // class Parent {}
p.__proto__ === Parent.prototype // true
Parent.prototype.__proto__ === Object.prototype  // true
Object.prototype.__proto__ === null  // true

每个对象拥有一个原型对象,通过proto指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null(Object.proptotype.proto 指向的是 null)。这种关系被称为原型链(prototypechain),通过原型链一个对象可以拥有定义在其他对象中的属性和方法。

- prototype 和 proto 区别

  • prototype 是构造函数的属性。
  • proto 是每个实例都有的属性,可以访问 [[prototype]] 属性。
  • 实例的__proto__ 与其构造函数的 prototype 指向的是同一个对象。
  • Jack.proto === Student.prototype
function Student(name) {
    this.name = name;
}
Student.prototype.setAge = function(){
    this.age=20;
}
let Jack = new Student('jack');
console.log(Jack.__proto__); //{setAge: ƒ, constructor: ƒ}
console.log(Jack.__proto__ === Student.prototype); //true
console.log(Object.getPrototypeOf(Jack)); //{setAge: ƒ, constructor: ƒ}
console.log(Student.prototype.__proto__ === Object.prototype); // true

- 使用 ES5 实现一个继承 todo

function SuperType() {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function() {
    console.log(this.name);
}

function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;

SubType.prototype.sayAge = function() {
    console.log(this.age);
}

- 防抖和节流的区别是什么?防抖和节流的实现。

防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于设置的时间,防抖的情况下只会调用一次,而节流的情况会每隔一定时间调用一次函数

防抖 (debounce): n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间。

function debounce(func, wait, immediate=true) {
    let timeout, context, args;
        // 延迟执行函数
        const later = () => setTimeout(() => {
            // 延迟函数执行完毕,清空定时器
            timeout = null
            // 延迟执行的情况下,函数会在延迟函数中执行
            // 使用到之前缓存的参数和上下文
            if (!immediate) {
                func.apply(context, args);
                context = args = null;
            }
        }, wait);
        let debounced = function (...params) {
            if (!timeout) {
                timeout = later();
                if (immediate) {
                    // 立即执行
                    func.apply(this, params);
                } else {
                    // 闭包
                    context = this;
                    args = params;
                }
            } else {
                clearTimeout(timeout);
                timeout = later();
            }
        }
    debounced.cancel = function () {
        clearTimeout(timeout);
        timeout = null;
    };
    return debounced;
};
- 防抖的应用场景
  • 每次 resize/scroll 触发统计事件;

  • 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)。

节流 (throttle): 高频事件在规定时间内只会执行一次,执行一次后,只有大于设定的执行周期后才会执行第二次。

//underscore.js
function throttle(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function () {
        previous = options.leading === false ? 0 : Date.now() || new Date().getTime();
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
    };

    var throttled = function () {
        var now = Date.now() || new Date().getTime();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
            } else if (!timeout && options.trailing !== false) {
            // 判断是否设置了定时器和 trailing
            timeout = setTimeout(later, remaining);
        }
        return result;
    };

    throttled.cancel = function () {
        clearTimeout(timeout);
        previous = 0;
        timeout = context = args = null;
    };

    return throttled;
};
- 函数节流的应用场景
  • DOM 元素的拖拽功能实现(mousemove);

  • 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹);

  • 计算鼠标移动的距离(mousemove);

  • Canvas 模拟画板功能(mousemove);

  • 搜索联想(keyup);

  • 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值