当前位置: 首页 > news >正文

刷题 | 牛客 - js入门15题(更ing)5/15知识点解答

知识点总结:

a.contains(b)---> a 节点中是否包含 b

a.parentNode()-----> 求a节点的父节点

Object.assign(target, ...sources)-----> 浅拷贝:只拷贝对象的第一层属性,内部嵌套对象依然是引用类型常用Object.assign(tar,sour):将所有源对象的可枚举属性复制到目标对象

防御性编程

链式赋值

fn(...arr)-----> 扩展运算符,注:arr为参数/集合(数组)
fn.apply(this,arr)-------->apply:修改 this的指向,一参是函数体内的this指向,二参是收集一个集合对象(数组和类数组)

fn.call(this, ...arr)--------->call:修改 this指向,一参 函数体内的this指向,二参是往后是依次传入的参数


JS41 dom 节点查找

描述

查找两个节点的最近的一个共同父节点,可以包括节点自身

输入描述:

oNode1 和 oNode2 在同一文档中,且不会为相同的节点

解题思路:

递归求解,一个节点不动,另一个节点不断向上查找,直到找到一个父节点包含另一个节点为止。

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <style>
       /* 填写样式 */
    </style>
</head>

<body>
    <!-- 填写标签 -->
    <script type="text/javascript">
        // 填写JavaScript
        function commonParentNode(oNode1, oNode2) {
            // 方法一:不断向上查找,直到找到一个父节点包含oNode2
            /* for(;;oNode1 = oNode1.parentNode){
                if(oNode1.contains(oNode2)){
                    return oNode1;
                }
            }*/
            // 方法二:递归求解。一个节点不动,另一个节点不断向上查找,直到找到一个父节点能够包含另一个节点为止
            if(oNode1.contains(oNode2)){
                return oNode1;
            }else{
                return commonParentNode(oNode1.parentNode,oNode2);
            }
    }
    </script>
</body>

</html>

JS44 根据包名,在指定空间中创建对象

描述

根据包名,在指定空间中创建对象

输入描述:

namespace({a: {test: 1, b: 2}}, 'a.b.c.d')

输出描述:

{a: {test: 1, b: {c: {d: {}}}}}


解题与分析

法一: 

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <style>
       /* 填写样式 */
    </style>
</head>

<body>
    <!-- 填写标签 -->
    <script type="text/javascript">
        function namespace(oNamespace, sPackage) {
            const arr = sPackage.split('.');
            let res = oNamespace
            // 如果不是对象,则使它成为对象
            for(let i=0;i<arr.length;i++){
                if(typeof res[arr[i]] != 'object'){
                    res[arr[i]] = {};
                }
                res = res[arr[i]];      // 不可省,在循环中将其赋给res
            }
            return res
            // return oNamespace        二者都可,因为复制的是对象的引用
        }
    </script>
</body>

法二,浅拷贝方法:

            // 法二:浅拷贝
            let o = oNamespace;
            let res = sPackage.split('.').forEach(item => {
                o[item] = o[item] ? Object.assign({}, o[item]) : {} ;
                o = o[item];
                });
            // return res;
            return o;

 知识点:

🔑 一、Object.assign() 细讲

Object.assign() 是 ES6 引入的方法,作用是将所有源对象的可枚举属性复制到目标对象,并返回目标对象。

语法:Object.assign(target, ...sources)
参数作用
target目标对象(会被修改并返回)
sources一个或多个源对象

2. 浅拷贝特性

浅拷贝只拷贝对象的第一层属性,内部嵌套对象依然是引用类型

示例:

let obj1 = { a: 1, b: { c: 2 } };

let obj2 = Object.assign({}, obj1);

obj2.a = 100; obj2.b.c = 200;

console.log(obj1.a); // 1 ✅(基本类型被拷贝)

console.log(obj1.b.c); // 200 ❌(引用类型被共享)

👉 说明:

  • a 是基本类型,直接拷贝值 ✅
  • b.c 是引用类型,拷贝的只是地址引用 🔥

3. 为什么用 Object.assign({}, tmpWrap[item])

假设代码里直接写:tmpWrap[item] = tmpWrap[item] || {};

如果 tmpWrap[item] 是对象:

  • 直接赋值会共用同一个对象的引用,修改 tmpWrap[item] 也会影响原始对象
  • Object.assign() 会创建一个新的对象,确保不会共享引用。

🚨 防御性编程核心原因

tmpWrap[item] = Object.assign({}, tmpWrap[item]); 

这个写法的优势:

写法是否安全原因
`tmpWrap[item] = tmpWrap[item]{};`
tmpWrap[item] = {};会覆盖已有数据
Object.assign({}, tmpWrap[item])创建新对象,数据隔离


🔥 二、额外思考的安全原因

代码:

tmpWrap[item] = tmpWrap[item] ? Object.assign({}, tmpWrap[item]) : {};

这个写法为什么更安全?

✅ 安全点在于:

  • ? 这个三元运算符确保只有在对象已经存在的情况下才会执行浅拷贝
  • 如果对象不存在,直接创建 {},避免多余的浅拷贝

性能对比

写法性能安全性说明
Object.assign({}, tmpWrap[item])较低每次都会浅拷贝
tmpWrap[item] ? Object.assign({}, tmpWrap[item]) : {}较高只有存在对象时才拷贝


★ 三、三个等号解释

代码:

tmpWrap = tmpWrap[item] = Object.assign({}, tmpWrap[item]);

等号的执行顺序是从右往左,相当于:

  1. Object.assign({}, tmpWrap[item]) 先执行,返回一个新对象
  2. tmpWrap[item] = 新对象
    • 将新对象赋值给当前层级的属性
  3. tmpWrap = 新对象
    • tmpWrap 移动到下一级,方便下一次迭代

🚀 执行顺序演示

假设:let obj = {}; namespace(obj, 'a.b.c');

执行过程:

步骤tmpWraptmpWrap[item]说明
初始{}-初始对象
a{}{}创建 a 属性
b{}{}创建 a.b 属性
c{}{}创建 a.b.c 属性


🔑 终极总结

知识点说明
浅拷贝避免共享引用
Object.assign()数据隔离 + 防御性编程
三个等号执行顺序代码链式赋值的重要特性
额外思考写法性能 + 安全性更高


💡 最佳代码写法

tmpWrap[item] = tmpWrap[item] ? Object.assign({}, tmpWrap[item]) : {}; tmpWrap = tmpWrap[item];

👉 这样写:

  1. 只有在需要时才进行浅拷贝
  2. 不会污染已有对象
  3. 代码可读性更好

本题考察的核心点:

  • 浅拷贝/深拷贝 的理解
  • 对象引用类型的陷阱
  • 链式赋值的执行顺序
  • 防御性编程思想

JS54 函数传参

描述

将数组 arr 中的元素作为调用函数 fn 的参数

示例1

输入:

function (greeting, name, punctuation) {return greeting + ', ' + name + (punctuation || '!');}, ['Hello', 'Ellie', '!']

复制输出:

Hello, Ellie!

 解题:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <style>
       /* 填写样式 */
    </style>
</head>

<body>
    <!-- 填写标签 -->
    <script type="text/javascript">
        // 填写JavaScript
        function argsAsArray(fn, arr) {
            // 法一:扩展运算符,防止fn不存在
            // return fn && fn(...arr);
            // 法二:apply 修改this的指向,一参是函数体内的this指向,二参是收集一个集合对象(数组和类数组)
            // return fn.apply(this,arr);
            // 法三:call 修改this指向,一参 函数体内的this指向,二参是往后是依次传入的参数
            return fn.call(this, ...arr);

        }
    </script>
</body>

</html>

相关文章:

  • ubuntu 启动不起来,光标闪烁 解决方法
  • 杰和科技工业整机AF208|防尘+静音+全天候运行
  • GPU/CUDA 发展编年史:从 3D 渲染到 AI 大模型时代
  • 谈谈 HTTPS 的工作原理,SSL / TLS 握手流程是什么?
  • RabbitMQ怎么实现延时支付?
  • C++:内联函数
  • Linux常用指令
  • VirtualBox虚拟机安装Mac OS启动后的系统设置
  • 指纹细节提取(Matlab实现)
  • Java 大视界 -- Java 大数据在智能教育考试评估与学情分析中的应用(112)
  • RV1126的OSD模块和SDL_TTF结合输出H264文件
  • Elasticsearch简单学习
  • 电子电路中,正负双电源供电的需求原因
  • excel 斜向拆分单元格
  • 第51天:Web开发-JavaEE应用SpringBoot栈身份验证JWT令牌Security鉴权安全绕过
  • Webpack、Vite区别知多少?
  • 单片机学习规划
  • Java学习——day14
  • 成功解决 “\ufeffimport sys“ SyntaxError: invalid character in identifier
  • Node.js setImmediate 教程
  • 同日哑火丢冠,双骄的下山路,手牵手一起走
  • 复星医药换帅:陈玉卿接棒吴以芳任董事长,吴以芳改任复星国际执行总裁
  • 邮储银行一季度净赚超252亿降逾2%,营收微降
  • 外交部亚洲司司长刘劲松向菲方严肃交涉
  • 五一假期上海推出首批16条“市民健康路线”,这些健康提示请收好
  • 中方发布《不跪!》视频传递何种信息?外交部回应