javascript中的原型和原型链&set和map&object.defineProperty和Proxy&object中的方法属性

一.原型

     原型: 原型也叫作“原型对象”,任何一个js对象都有与之对应的原型

     获取对象的原型:对象 _proto_

      比如构造函数Tank,new一个实例对象t,求实例对象t的原型:t._proto_得到{constructor...}

对于对象{constructor...}再对它constructor,得到构造函数Tank,而对构造函数Tank求原型对象得到{constructor...},所以我们可以得到以下关系:

     对象._proto_==函数.prototype

     对象._proto_.constructor==函数

     对象._proto_.constructor=函数.prototype.constructor

     获取对象原型:函数.prototype

 二.原型链

          通过对象的__proto__属性一级一级向上查找得到一个有_proto_构成的链条,这个链条就是原型链。

          原型链的终点是object的原型

          Object是所有对象的顶层构造函数,即所有的对象都可以看做是由Object创建的

          instanceof用来判断一个实例对象是否是构造函数的实例

三.set以及set的应用

   set的应用:

<script>
        //Set中数据不会重复
        var s = new Set([1,2,3,4,5,6])
        s.add(1); //添加元素到 set,1存在添加不上
        //s.delete(3); //从set删除元素,
        if( s.has(2) ){  //判断某个元素是否在set中
            console.log('2存在');
        }
        s.forEach((item,index)=>{
            console.log(item,index);
        })
        // s.clear(); //清空 set
        console.log(s);
        console.log(s.size);
</script>

  set去重:

       //数组去重
        var arr = [1,2,3,4,4,3,2,1];
        //写法一
        var s = [...new Set(arr)];
        console.log(s);
        
        //写法二
        var s = Array.from( new Set(arr) )
        console.log(s);

  Map:

    <script>
        //Map是种数据结构,类似对象
        var m = new Map();
        m.set('name','李煜'); 
        //向map结构 添加数据
        m.set('age',30);
        m.set('sex',false);
        console.log( m.get('name') , m.get('age') , m.get('sex') );
        //从map结构中获取数据
        console.log( m.has('age') );
        m.forEach((item,key)=>{
            console.log(item,key);
        })
        console.log(m);
    </script>

4.object.defineProperty和Proxy

      区别:defineproPerty是对属性的劫持,给对象的某个属性进行监听。proxy是对整个对象的代理,如果需要监听某一个对象的所有属性,需要遍历对象所有属性并对其进行劫持来进行监听。

defineProperty:无法监听对象新增属性,proxy可以;

defineProperty无法监听对象的删除属性,proxy可以;

defineProperty不能监听数组下标改变值的变化,proxy可以且不需要对数组方法进行重载;

defineProperty是循环遍历对象属性的方式来进行监听,自然会比proxy对整个对象进行监听的方式要消耗性能。

 var obj = { name:'李煜',age:30, father:{ name:'',age:90, son:{name:'',age:10} } };
        //writable 对象的属性是否可修改 ( 修改值 )
        //configurable 对象的属性是否可操作 ( 删除 )
        //enumerable 对象的属性是否可枚举 ( 遍历 )

        Object.defineProperty(obj, 'name', { writable:true })
        Object.defineProperty(obj, 'name', { configurable:true })
        Object.defineProperty(obj, 'name', { enumerable:true })
        Object.defineProperty(obj, 'name', { 
            // writable:true,
            set: function( newValue ){ 
            //在set函数中不能 执行针对该属性的赋值操作,会导致函数的递归调用.
                console.log('修改了name属性的值',newValue); 
            },
            get: function(){ 
            //在get函数中不能 执行获取该属性的操作, 会导致函数的递归调用.
                console.log('获取了name属性的值');
                return '李煜'+'!!!';
            },
        })
 var obj = { name:'李煜',age:30, father:{ name:'',age:90, son:{name:'',age:10} } };
 //监听对象的所有属性
        //思路:
        //1.先用for in 遍历对象, 拿到对象的每个属性
        //2.用Object.defineProperty给对象的每个属性设置监听

        //作用: 传入一个对象, 对该对象的每个属性进行遍历并且监听.
        function listenObj(obj){
            for(var key in obj){
                if( typeof obj[key] == 'object' ){ 
        //如果某个属性是引用类型, 则递归遍历该属性
                    listenObj(obj[key]) //递归遍历该属性
                }else{ //不是引用类型
                    Object.defineProperty(obj, key , {
                        set:()=>{ console.log('修改了属性的值');  },
                        get:()=>{ console.log('获取了属性的值'); }
                    })
                }
            }
        }

        listenObj(obj)

 5.object中的方法属性

         注意:当调用对象的某个方法时, 首先在对象自身找该方法,如果自身有该方法则调用成功,如果没有则继续沿着由__proto__构成的原型链查找该方法, 如果一直到原型链的终点都没有找到则报错.

 以下为存在于Object中的,是可以找见的方法:

        Object.prototype.constructor

        Object.prototype.toString()

        Object.prototype.toLocaleString()

        Object.prototype.hasOwnProperty()

        Object.prototype.isPrototypeOf()

  判断一个属性是否在对象身上:

        hasOwnProperty() 在判断一个属性是否在对象身上时, 不会沿着原型链查找的(只会在对象自身身上查找。)

var obj = { name:'李煜',age:30 }
if( obj.hasOwnProperty('toString') ){
        // if( 'toString' in obj ){
            console.log('在obj身上');
        }else{
            console.log('不在obj身上');
        }
//输出不在

        in 在判断一个属性是否在对象身上时, 会沿着原型链查找

        isPrototypeOf() 判断一个对象 是否是某个函数的原型对象

        结论: js中任何一个对象都可以看做是 Object这个顶层构造函数的实例对象、

       if( Array.prototype.isPrototypeOf( Object ) ){
            console.log('是');
        }else{
            console.log('不是');      
        }
       //打印结果为不是,因为object的原型对象为null

       if( Object.prototype.isPrototypeOf( RegExp ) ){
         console.log('是');
        }else{
            console.log('不是');      
        }
       //打印结果为是,因为js中任何一个对象都可以看做是 Object这个顶层构造函数的实例对象

<think>好的,我现在需要回答用户关于防止JavaScript原型链污染漏洞的最佳实践安全解决方案的问题。首先,我得回忆一下原型链污染的基本概念,以及它是如何发生的。原型链污染通常发生在攻击者能够修改对象的原型时,比如通过修改__proto__属性,或者在合并对象时未正确过滤输入。 接下来,我需要考虑用户提供的引用内容。引用[1]提到了Lodash的安全漏洞,这应该是一个实际的例子,说明原型链污染可能导致的问题。引用[2][3]讨论了原型链污染的利用方式如何访问原型链,这有助于理解攻击者的手法。引用[4]提到如何获取原型对象,这可能与检测方法有关。引用[5]提醒注意文档的使用目的,但可能对解决方案没有直接影响。 用户需要的是最佳实践解决方案,所以应该从预防、检测修复几个方面来组织答案。首先,预防措施可能包括避免使用不安全的合并函数、冻结原型对象、使用Map代替普通对象等。然后,检测方法可以涉及静态代码分析运行时监控。最后,修复已经存在的漏洞可能需要更新依赖库或实施输入过滤。 需要注意的是,确保建议具体且实用,比如推荐使用Object.freeze()来冻结原型,或者使用Lodash的安全版本。同时,引用中的案例(如Lodash漏洞)可以作为例子,说明这些措施的必要性。另外,要提到输入验证过滤的重要性,特别是在处理用户提供的JSON数据时,避免将不可信的属性合并到对象中。 可能还需要考虑现代JavaScript的特性,比如使用Proxy对象来监控原型访问,但这可能比较复杂。同时,提到使用ES6类语法可能有助于避免无意中修改原型。 还要检查是否有遗漏的点,比如是否提到了依赖库的更新漏洞监控,以及代码审查的重要性。需要确保每个建议都有相应的引用支持,比如引用[1][3]中的内容。 最后,组织答案的结构,使其清晰易懂,分点列出每个措施,并附上解释例子。这样用户能够逐步了解如何实施这些安全措施,从而有效防止原型链污染漏洞。</think>### 防止 JavaScript 原型链污染漏洞的安全措施 #### 1. **避免直接操作原型属性** - **禁止直接修改 `__proto__` 或 `prototype`** 开发者应避免通过 `obj.__proto__` 或 `constructor.prototype` 直接操作对象的原型链。这种操作可能被攻击者利用来注入恶意属性[^3][^4]。 - **使用 `Object.create(null)` 创建无原型对象** 对于需要纯粹键值对的场景,使用 `Object.create(null)` 创建无原型链的对象,避免继承 `Object.prototype` 的属性。 ```javascript const safeObj = Object.create(null); safeObj.key = "value"; // 无原型链污染风险 ``` #### 2. **安全处理对象合并操作** - **避免使用不安全的递归合并函数** Lodash 的 `_.merge` 等函数曾因未过滤 `__proto__` 导致原型链污染漏洞[^1]。建议: - 使用经过安全修复的库版本(如 Lodash >= 4.17.12)。 - 自定义合并逻辑时,过滤 `__proto__`、`constructor` 等敏感属性。 ```javascript function safeMerge(target, source) { for (const key in source) { if (key !== '__proto__' && key !== 'constructor' && key !== 'prototype') { target[key] = source[key]; } } return target; } ``` #### 3. **冻结原型对象** - **使用 `Object.freeze()` 防止原型修改** 对内置原型(如 `Object.prototype`、`Array.prototype`)进行冻结,阻止属性注入: ```javascript Object.freeze(Object.prototype); Object.freeze(Array.prototype); ``` - **注意**:冻结可能影响第三方库的兼容性,需谨慎评估。 #### 4. **输入过滤与验证** - **严格校验用户输入的 JSON 数据** 对用户提交的 JSON 对象进行属性白名单过滤,拒绝包含 `__proto__`、`constructor` 等关键字的输入。 - **使用安全的反序列化工具** 避免使用 `JSON.parse` 直接解析未经验证的输入,可选用库如 `JSON5` 或自定义解析逻辑。 #### 5. **静态代码分析与运行时监控** - **使用 ESLint 插件检测风险代码** 配置规则(如 `no-proto`、`no-extend-native`)禁止直接操作原型链。 - **运行时监控原型链修改** 通过 `Proxy` 或 `Object.defineProperty` 监听原型属性的写入操作: ```javascript const handler = { set(target, prop, value) { if (prop === '__proto__') throw new Error("Prototype modification blocked"); return Reflect.set(...arguments); } }; const monitoredObj = new Proxy({}, handler); ``` #### 6. **依赖管理与漏洞监控** - **及时更新第三方库** 定期检查依赖库的安全公告(如 Lodash 的原型链污染修复版本)。 - **使用工具扫描漏洞** 集成 `npm audit`、Snyk 等工具,自动化检测项目中的已知漏洞。 #### 7. **使用现代 JavaScript 特性替代** - **优先使用 `Map` 代替 `Object`** `Map` 数据结构不继承自 `Object.prototype`,能减少原型链污染风险。 - **Class 语法替代原型继承** 通过 ES6 Class 实现继承,避免直接操作 `prototype`。 --- ### 示例攻击与防御对比 **攻击场景**: ```javascript // 恶意输入尝试污染原型链 const maliciousPayload = JSON.parse('{"__proto__": {"isAdmin": true}}'); _.merge({}, maliciousPayload); // 旧版 Lodash 会污染原型 ``` **防御措施**: 更新 Lodash 至安全版本,或使用自定义的 `safeMerge` 函数过滤 `__proto__`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值