前言

之前打某湖论剑,两道js的题,给我整懵逼了,发现以前都没对js做过多少研究,趁着被毒打了,先研究一波js原型链,未雨绸缪。

基础

protype

首先我们研究js原型链,得搞明白原型是什么,这里借用p神的举的一个例子:

在javascript中,我们定义一个类,需要以定义“构造函数”的方式来定义:

function Foo() {
this.bar = 1
} new Foo()

Foo()函数的内容就是构造函数的内容,this.bar是Foo的一个属性,学过c++的应该很容易理解,而且后面对原型链的利用也可以仿造c++类的思想来理解。

一个类必然有方法,我们可以在构造函数里定义方法:

function Foo() {
this.bar = 1
this.show = function() {
console.log(this.bar)
}
}

在js里,这样定义有一个特点,就是这个方法并不是绑定在类上,而是每创建一个对象,这个方法就会定义一次,这样就很浪费资源,我们要让它只定义一次,就需要用到原型prototype,这个prototype可以认为是一个类的属性,通过类创建的对象都将"继承"prototype的内容。

function Foo() {
this.bar = 1
} Foo.prototype.show = function show() {
console.log(this.bar)
}

__proto__

那么__proto__是干什么用的?原来由类实例化的对象是无法直接访问到类的原型也就是prototype,我们可以看到foo没有prototype

这时__proto__就是对象访问类原型的媒介了

以上大概就是原型的内容,读者把原型的概念弄懂后再去看原型链污染会有不一样的结果

原型链继承

以上讲的prototype在js里其实主要是用来实现继承机制,这个继承跟c++的继承不太一样,js的继承可以改掉Object类导致原型链污染,而c++的继承是可以对父类的方法进行进行重写或重载,但不会直接就把父类给改写了,因为这个特性,这才有了原型链污染的诞生。这里举个例子:

function Father() {
this.first_name = 'Donald'
this.last_name = 'Trump'
} function Son() {
this.first_name = 'Melania'
} Son.prototype = new Father() let son = new Son()
console.log(`Name: ${son.first_name} ${son.last_name}`)

这里Son继承了Father的last_name

这里通过__proto__给Object添了个last_name

这里推导一下:

son.__proto__ == Son.prototype

son.__proto__.__proto__ == Father.prototype

son.__proto__.__proto__.__proto__ == Object

所以后面我们new的a,它有一个last_name是继承Object的,这里有趣的是,改写了Object后,Father.last__name == john,但是son.last_name == Trump,直接看Father函数,其实它的last_name并没有变化

这就有趣了,但重新new Father,它的last_name其实也是没变的

但我们还是成功污染了的,Object多了个last_name属性,此后新建的类都将继承这个属性,在某些地方是会造成危害的

原型链污染实例

在什么地方我们可以使用原型链污染?其实当有我们可以控制的"键名",并存在有赋值修改操作的地方,我们可以实现这个操作,这里举个js的merge函数经典例子:

function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}

以上是一个简单的合并函数,我们可以看到target[key] = source[key],其实这个地方就存在原型链污染,如果key是__proto__的话,是不是就能修改到Object,这里给个例子:

let o1 = {}
let o2 = {a: 1, "__proto__": {b: 2}}
merge(o1, o2)
console.log(o1.a, o1.b) o3 = {}
console.log(o3.b)

这里我们合并成功了,但是并没有污染到Object,原因是__proto__没有被当成键名,而是当成原型,也就是遍历键名的时候只有a,b是键名。

为了让__proto__也被当成键名,我们可以把o2的值设置成json的格式,遍历json的时候,__proto__就会被当成键名,从而改写Object,例子如下:

let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b) o3 = {}
console.log(o3.b)

成功改写Object,导致o3有了b属性

总结

以上就是js原型链的基础内容,把基础打好,之后遇见原型链的题将会成长很快。

js原型链污染详解的更多相关文章

  1. JS 原型链图形详解

    JS原型链 这篇文章是「深入ECMA-262-3」系列的一个概览和摘要.每个部分都包含了对应章节的链接,所以你可以阅读它们以便对其有更深的理解. 对象 ECMAScript做为一个高度抽象的面向对象语 ...

  2. 最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)

    最详尽的 JS 原型与原型链终极详解,没有「可能是」.(一) 第二篇已更新,点击进入第三篇已更新,点击进入

  3. JS原型与原型链终极详解(转)

    JavaScript原型及原型链详解 一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object,Function 是JS自带的函数对象. ...

  4. 【repost】JS原型与原型链终极详解

    一. 普通对象与函数对象  JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object ,Function 是JS自带的函数对象.下面举例说明 function f ...

  5. JS原型与原型链终极详解

    一. 普通对象与函数对象  JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object ,Function 是JS自带的函数对象.下面举例说明 function f ...

  6. js重点--原型链继承详解

    上篇说过了关于原型链继承的问题,这篇详解一下. 1. function animals(){ this.type = "animals"; } animals.prototype. ...

  7. [转] 最详尽的 JS 原型与原型链终极详解

    四. __proto__ JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的原型对象. 对象 person1 有一个 __pr ...

  8. JS原型与原型链终极详解 (转载)

    这篇文章需要认认真真仔仔细细的看才能看懂 一. 普通对象与函数对象  JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object ,Function 是JS自带的函 ...

  9. JS 原型与原型链终极详解(二)

    四. __proto__ JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的原型对象. 对象 person1 有一个 __pr ...

  10. 最详尽的 JS 原型与原型链终极详解(1)(2)(3)===转载

    转载===方便以后复习 原文网址:https://www.jianshu.com/p/dee9f8b14771 一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为 ...

随机推荐

  1. vulnhub靶场之HACK ME PLEASE

    准备: 攻击机:虚拟机kali.本机win10. 靶机:HACK ME PLEASE,下载地址:https://download.vulnhub.com/hackmeplease/Hack_Me_Pl ...

  2. 【每日一题】【判断栈是否为空的方法】2022年1月9日-NC76 用两个栈实现队列的出队入队【入队简单】

    描述用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能. 队列中的元素为int类型.保证操作合法,即保证pop操作时队列内已有元素 ...

  3. 服务器迁移遇到的bug

    目前有个客户做了个下单系统,系统运行了太多的扩展了,但是又没有文档,我就想着能不能把服务器打包成镜像,然后用新服务直接使用?? 事实是完全OK的.服务器用的阿里云. 但是遇到一个bug,我目前还没解决 ...

  4. 更改jenkins的工作目录

    1.原始工作空间 2.目的盘符 3.任务管理器,找到Jenkins邮件转到详细信息 4.找到jenkins.exe打开文件所在位置 5.找到jenkins.xml打开 6.修改value值 改前: 改 ...

  5. JavaScript:变量:如何声明变量?

    声明变量可以用下面几种方式: 但是这几种声明方式肯定是有区别的,主要是上面三种方式的区别,这需要结合window对象和作用域来说明,这里不赘述. 声明变量的时候,推荐使用let,这是ES6新推出的更好 ...

  6. 4、Idea设置显示多行文件

    使用IDEA时,可能会没有注意到,一旦打开过多的Java文件时,默认会堆积在一行显示,就像浏览器打开了多个标签一样,此时需要通过右侧箭头筛选的方式来选择其他文件.为了解决这一问题,需要打开多行显示的方 ...

  7. c#5.0(.net 4.5之后)的 Async+await+Task的异步机制的调试笔记

    1.)无返回值的情况(异步也是基于线程). using System; using System.Collections.Generic; using System.Linq; using Syste ...

  8. [seaborn] seaborn学习笔记8-避免过度绘图Avoid Overplotting

    8 避免过度绘图Avoid Overplotting(代码下载) 过度绘图是散点图及几天常见图表中最常见的问题之一.如下图所示当数据集很大时,散点图的点会重叠,使得图形变得不可读.在这篇文章中,提出了 ...

  9. Java基于内存的消息队列实现

    Java基于内存的消息队列实现 有需要源码的同学可以去Gitee上下载: https://gitee.com/hyxl-520/memory_message_queue 先看测试情况 需求背景 需求来 ...

  10. linux下redis_单机版_主从_集群_部署文档

    一 单机版部署 1.1 Redis下载地址 http://download.redis.io/releases/ 本次部署版本:3.2.8 当前最新版本:5.0.5 1.2 安装 部署路径说明规划 / ...