JavaScript手撕前端面试题:手写new操作符 | 手写Object.freeze

Posted 海底烧烤店ai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript手撕前端面试题:手写new操作符 | 手写Object.freeze相关的知识,希望对你有一定的参考价值。

🖥️ NodeJS专栏:Node.js从入门到精通
🖥️ 博主的前端之路(源创征文一等奖作品):前端之行,任重道远(来自大三学长的万字自述)
🖥️ TypeScript知识总结:TypeScript从入门到精通(十万字超详细知识点总结)
🧑‍💼个人简介:大三学生,一个不甘平庸的平凡人🍬
👉 你的一键三连是我更新的最大动力❤️!
🏆分享博主自用牛客网🏆:一个非常全面的面试刷题求职网站,点击跳转🍬


文章目录

前言

向大家推荐一款博主一直在用的面试刷题求职网站:牛客网

牛客网不仅具有公司真题专项练习面试题库在线编程等功能,还具有非常强大的AI模拟面试功能,简直是求职者的福音!

牛客网里的题库非常全面的,无论你是前端还是后端,是想要备考还是准备面试又或者是想要提高自己,你都能在牛客网上找到适合自己的题,赶快点击链接去注册登录吧:点击进入牛客网

牛客网牛客网

本篇文章所有示例参考自牛客网题库/在线编程/JS篇

1、手写new操作符

要求

补全javascript代码,要求实现new操作符的功能。


new 运算符介绍如下:

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

参数:

  • constructor
    一个指定对象实例的类型的类或函数。

  • arguments
    一个用于被 constructor 调用的参数列表。

new 关键字会进行如下的操作:

  1. 创建一个空的简单 JavaScript 对象(即 );
  2. 为步骤 1 新创建的对象添加属性 __proto__,将该属性链接至构造函数的原型对象(设置它的原型为构造函数的原型对象);
  3. 将步骤 1 新创建的对象作为 this 的上下文;
  4. 如果该函数没有返回对象,则返回 this

手撕代码

const _new = function () 
    // 补全代码
    // 1. 创建一个空的简单 `JavaScript` 对象(即 ``);
    // 2. 为步骤 1 新创建的对象添加属性 `__proto__`,将该属性链接至构造函数的原型对象(设置它的原型为构造函数的原型对象);
    // 3. 将步骤 1 新创建的对象作为 `this` 的上下文;
    // 4. 如果该函数没有返回对象,则返回 `this`。
    const obj1 = ; // 1
    const Fn = arguments[0]; // 获取函数参数
    Object.setPrototypeOf(obj1, Fn.prototype); // 2 
    const obj2 = Fn.apply(obj1, [].slice.call(arguments, 1)) // 3 
    return obj2 instanceof Object ? obj2 : obj1; // 4


[].slice.call(arguments, 1)的作用是获得一个arguments的拷贝数组,且该数组不含arguments的第一个元素。

我们知道数组的slice方法能够截取原数组的部分内容(返回一个新数组,不会修改原数组),但参数数组arguments是一个伪数组并不具有slice这个方法,所以这里使用call[].slicethis指定到arguments

测试一下:

function Fn(age) 
    this.name = 'Ailjx'
    this.age = age

console.log('new', new Fn(18));
console.log('_new', _new(Fn, 18));

2、手写Object.freeze

要求

补全JavaScript代码,要求实现Object.freeze函数的功能且该新函数命名为"_objectFreeze"


Object.freeze()函数介绍如下:

Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改freeze() 返回和传入的参数相同的对象。

参数:

  • obj
    要被冻结的对象。

返回值:

  • 被冻结的对象。

手撕代码

需要注意的是:

  1. 注意不可枚举的属性也要重新冻结。
  2. 注意 Symbol 类型作为 key 值的情况,也要冻结。
  3. 注意只冻结对象自有的属性(使用 for ... in 会把原型链上的可枚举属性遍历出来)。
  4. 注意不可扩展性(不能添加新属性,使用 Object.preventExtensions()搭配configurable: falseObject.seal() 实现,同时也相当于把原型链冻结)。
const _objectFreeze = object => 
    // 补全代码
    if (typeof object !== 'object' || object === null) 
        throw new TypeError(`the $object is not a object`)
    

    // Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。
    const keys = Object.getOwnPropertyNames(object);

    // Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。
    const symbols = Object.getOwnPropertySymbols(object);
    
    [...keys, ...symbols].forEach(key => 
        // Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
        Object.defineProperty(object, key, 
            // 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
            // configurable: false, // 如果下面使用的是Object.preventExtensions(object)而不是Object.seal(),则需要设置configurable: false
            
			// 当 writable 属性设置为 false 时,该属性被称为“不可写的”。它不能被重新赋值
            writable: false,
        )
    )

    // Object.seal()方法封闭一个对象,
    // 阻止添加新属性并将所有现有属性标记为不可配置。
    // 当前属性的值只要原来是可写的就可以改变。
    // 不会影响从原型链上继承的属性。但 __proto__ ( 已弃用 ) 属性的值也会不能修改。
    // Object.seal的效果相当于: 在Object.defineProperty时将configurable设置成false,同时对对象调用Object.preventExtensions。
    Object.seal(object)

    // Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
    // 该方法使得目标对象的 [[prototype]] 不可变;任何重新赋值 [[prototype]] 操作都会抛出 TypeError 。这种行为只针对内部的 [[prototype]] 属性,目标对象的其它属性将保持可变。
    // Object.preventExtensions(object) 
    
    return object

知识点:

  • Object.getOwnPropertyNames() 方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性不包括 Symbol 值作为名称的属性)组成的数组。
  • Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。
  • Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
  • Object.seal() 方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。效果相当于: 在Object.defineProperty时将configurable设置成false,同时对对象调用Object.preventExtensions
  • Object.preventExtensions() 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
    • Object.preventExtensions只防止添加属性,即不可扩展。
    • Object.seal除了不可扩展,也不可配置。
    • Object.freeze就像freeze的意思是被冻结,除了不可扩展,不可配置,也不可重写。

结语

这篇文章的所有内容都出自于牛客网的JS篇题库

牛客网的JS题库非常贴合实际的,在写的过程查漏补缺能收获了很多,强烈将牛客网推荐给大家!

如果本篇文章对你有所帮助,还请客官一件四连!❤️

基础不牢,地动山摇! 快来和博主一起来牛客网刷题巩固基础知识吧!

JavaScript手撕前端面试题:手写Object.create | 手写Function.call | 手写Function.bind

🖥️ NodeJS专栏:Node.js从入门到精通
🖥️ 博主的前端之路(源创征文一等奖作品):前端之行,任重道远(来自大三学长的万字自述)
🖥️ TypeScript知识总结:TypeScript从入门到精通(十万字超详细知识点总结)
🧑‍💼个人简介:大三学生,一个不甘平庸的平凡人🍬
👉 你的一键三连是我更新的最大动力❤️!
🏆分享博主自用牛客网🏆:一个非常全面的面试刷题求职网站,点击跳转🍬


文章目录

前言

向大家推荐一款博主一直在用的面试刷题求职网站:牛客网

牛客网不仅具有公司真题专项练习面试题库在线编程等功能,还具有非常强大的AI模拟面试功能,简直是求职者的福音!

牛客网里的题库非常全面的,无论你是前端还是后端,是想要备考还是准备面试又或者是想要提高自己,你都能在牛客网上找到适合自己的题,赶快点击链接去注册登录吧:点击进入牛客网

牛客网牛客网

本篇文章所有示例参考自牛客网题库/在线编程/JS篇

1、手写Object.create

要求

补全JavaScript代码,要求实现Object.create函数的功能且该新函数命名为"_objectCreate"。

Object.create函数介绍如下:

Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。

Object.create 参数:

  • proto
    新创建对象的原型对象。

  • propertiesObject (可选)
    如果该参数被指定且不为 undefined,则该传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符。这些属性对应于 Object.defineProperties() 的第二个参数。

Object.defineProperties()方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。

Object.create 返回值:一个新对象,带着指定的原型对象及其属性。

手撕代码

const _objectCreate = function (proto, propertiesObject) 
    // 补全代码
    if (typeof proto !== 'object' || proto === null) 
       throw new TypeError('Object prototype may only be an Object or null')
    
  
  	// 定义新对象 
    const obj = 
    
    // 设置原型
    // obj.__proto__ = proto // 不建议这么做了
    // 通常,应该使用 Object.setPrototypeOf() 方法来设置对象的原型。
    // 因为 Object.prototype.__proto__ 访问器已被弃用。
    
    Object.setPrototypeOf(obj, proto) // 建议使用setPrototypeOf设置原型
    
    if (propertiesObject && propertiesObject !== 'undefined') 
    	// 设置属性
        Object.defineProperties(obj, propertiesObject)
    

    return obj

注意:通过__proto__设置原型的方法(obj.__proto__ = proto)已经不在标准中了!

测试一下:

let o =  a: 1 
let b = _objectCreate(o, 
    'property1': 
        value: true,
        writable: true
    ,
    'property2': 
        value: 'Hello',
        writable: false
    
)
let c = Object.create(o, 
    'property1': 
        value: true,
        writable: true
    ,
    'property2': 
        value: 'Hello',
        writable: false
    
)

console.log('使用_objectCreate创建的:', b);
console.log('使用create创建的:', c);

2、手写Function.call

要求

补全JavaScript代码,要求实现Function.call函数的功能且该新函数命名为"_call"。

Function.call()函数介绍如下:

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

Function.call 参数:

  • thisArg
    可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,原始值会被包装。

  • arg1, arg2, ...
    指定的参数列表。

Function.call返回值:

使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined

手撕代码

// target参数默认为window
Function.prototype._call = function (target = window, ...arg) 
    // target是一个对象,这里的this就是调用_call方法的函数(母函数)
    // 我们需要实现的是将母函数的this指向到target,那么就可以:
    // 将母函数绑定到target的fn属性上,这样调用target.fn时它的this就指向了target
    target.fn = this;
    // 获取函数运行结果
    const result = target.fn(...arg);
    // 再将target上的fn属性删除
    delete target.fn
    return result

测试一下:

function fn(a, b) 
    return this.name + a + b

let obj =  name: 'Ailjx' 
console.log(fn.call(obj, 'S', "R"));
console.log(fn._call(obj, 'S', "R")); 

3、手写Function.bind

要求

补全JavaScript代码,要求实现Function.bind函数的功能且该新函数命名为"_bind"。

Function.bind()函数介绍如下:

Function.bind()函数与Function.call()函数类似,区别就是Function.bind()返回的是一个修改过this指向的新函数,而不是函数执行后的

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的初始参数,供调用时使用

Function.bind 参数:

  • thisArg
    调用绑定函数时作为 this 参数传递给目标函数的值。如果使用new运算符构造绑定函数,则忽略该值。当使用 bindsetTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者thisArgnullundefined,执行作用域的 this 将被视为新函数的 thisArg

  • arg1, arg2, ...
    当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

Function.bind返回值:

返回一个原函数的拷贝,并拥有指定的 this 值和初始参数

手撕代码

Function.prototype._bind = function (target = window, ...arg) 
    const fn = this;
    return function (...rest) 
        return fn.call(target, ...arg, ...rest);
    ;
;

测试一下:

function fn(a, b) 
    return this.name + a + b;

let obj =  name: "Ailjx" ;
// bind的第二个参数和返回的函数的参数会合并到一起
console.log(fn.bind(obj, "S")("R"));
// 以下两种写法与上面相等
// console.log(fn.bind(obj, "S", "R")());
// console.log(fn.bind(obj)("S", "R"));

console.log(fn._bind(obj, "S")("R"));
// console.log(fn._bind(obj, "S", "R")());
// console.log(fn._bind(obj)("S", "R"));

结语

这篇文章的所有内容都出自于牛客网的JS篇题库

牛客网的JS题库非常贴合实际的,在写的过程查漏补缺能收获了很多,强烈将牛客网推荐给大家!

如果本篇文章对你有所帮助,还请客官一件四连!❤️

基础不牢,地动山摇! 快来和博主一起来牛客网刷题巩固基础知识吧!

以上是关于JavaScript手撕前端面试题:手写new操作符 | 手写Object.freeze的主要内容,如果未能解决你的问题,请参考以下文章

前端面试题 ---- 手撕JavaScript call apply bind 函数(超详细)

手撕前端面试题(Javascript~事件委托数组去重合法的URL快速排序js中哪些操作会造成内存泄漏......

手撕前端面试题JavaScript

手撕前端面试题javascript

手撕前端面试题javascript~ 总成绩排名子字符串频次统计继承判断斐波那契数组等

手撕前端javascript面试题---快速排序 | 全排列 | instanceof