所谓裸对象,即 naked object ,是指没有原型(spec中以[[proto]]内建属性表示)的对象。
JavaScript是少见的采用原型继承的语言。访问一个对象的属性时,会首先看它自己的属性,所谓 own property 是也,如果找不到,则在其原型中查找,再找不到就继续找这个原型的原型,这就构成所谓的原型链。
原型继承提供了一种很独特的共享信息的方式,不过也带来一些有趣的问题。比如with构造。
我在2011年w3ctech广州的演讲中提到过with构造的问题,所以在strict模式中with就被禁用了。
其中一个问题是,with(obj)时,obj如果是你
不可掌控的对象,会引入无法控制的风险。所谓不可掌控,例如浏览器对象(像在演讲中举的event对象),或者第三方库的对象或可能被第三方库修改的对象(例如DOM对象就是如此,许多库会在上面加各种东西)。
这和原型有什么关系?很有关系!因为with不是仅仅查找own property,而是也会上溯原型链。
例子:
var name = 'hax'
with (console) {
log('Hello ' + name)
}
注意,许多实现里 console.log 的 log 方法并非直接在 console 对象上,而是在 console 的原型上。
如此看上溯原型链似乎是件好事,但是考虑这个代码:
var name = 'hax', count = 3
with (console) {
for (var i = 0; i < count; i++)
log('Hello ' + name)
}
这代码在有些环境可以跑,如NodeJS;但在Chrome里就不行了,会死循环(我没高兴实验,欢迎小白鼠尝试)。虽然大家用的都是V8引擎,但是Chrome里的console上有一个count()方法。
更讨厌的是,几乎所有对象都有原型,一直到Object.prototype。考虑如下代码:
function constructor() {
...
}
with ({}) {
var x = constructor()
}
实际结果等价于 x = Object() ,因为 Object.prototype.constructor == Object 。
这也导致修改Object.prototype就可能干扰所有的 with 块。而Object.prototype是所有对象的原型,换句话说,几乎所有情况下都是
不可掌控的。
【注意,确实是存在不以 Object.prototype 为原型的对象,比如某些环境里的host对象,但是显然它们也不属于你可掌控的对象。】
可见with的最初设计就有失误,如果是仅仅查找 own property 会好很多。不过现在已经不可能做这样的修改,所以只能想其他方式,比如如果我们真正的有 naked object,那么就可以做到对对象的完全掌控。
听上去不太可能,除了null和undefined,所有对象(包括number、string和boolean通常会自动装箱成包装对象)都是Object的实例,也就是有Object.prototype作为原型链的最末端。
不过ES5所增加的Object.create()方法就提供了这样一个能力!只要调用Object.create(null)就可以得到一个裸对象!这这不错。所以我们得到了一个相对安全的with方式如下:
with (Context({a: 1, b: 2})) {
console.log(a, b)
}
function Context(obj) {
var pds = {}
var names = Object.getOwnPropertyNames(obj)
for (var i = 0; i < names.length; i++) {
pds[names[i]] = Object.getOwnPropertyDescriptor(obj, names[i])
}
return Object.create(null, pds)
}
不过,对于老的,没有Object.create方法的浏览器怎么办?
许多浏览器有 __proto__ 属性可以直接访问对象的 __proto__ 。所以NCZ写过一篇关于裸对象的文章:
Naked JavaScript objects就提到这个方式。看上去其实比Object.create还要简单:
with({a: 1, b: 2, __proto__: null}) { ... }
不过还有一些老浏览器不支持 __proto__ ,例如IE。怎么办呢?
其实有办法。
可以发现,在整个JavaScript对象世界里,似乎所有的对象都有原型,但是有一个例外。
没错,那就是所有对象之源:Object.prototype 。它自己是没有原型的!
var root = Object.prototype
Object.getPrototypeOf(root) === null // true
如果我们把Object.prototype上的所有属性都delete掉,我们就得到了一个裸对象!
不过这样把Object.prototype给破坏,显然会影响我们的程序。好在咱有 iframe !每个iframe的执行环境是独立的!于是可以每次创建一个iframe,把它里面的Object.prototype给拿来用!(听上去挺浪费的是吧……)
代码如下:
function nakedObject() {
var iframe = document.createElement('iframe')
iframe.width = iframe.height = 0
iframe.style.display = 'none'
document.appendChild(iframe)
iframe.src = 'javascript:'
var proto = iframe.contentWindow.Object.prototype
iframe.parentNode.removeChild(iframe)
iframe = null
var props = [
'constructor', 'hasOwnProperty', 'propertyIsEnumerable',
'isPrototypeOf', 'toLocaleString', 'toString', 'valueOf'
]
for (var i = 0; i < props.length; i++) {
delete proto[props[i]]
}
return proto
}
不过这个技巧确实相当浪费,仅仅为了创建一个对象就建立了一整个iframe及其环境!那可相当于一个完整的网页!在IE中创建几千个这样的对象,就需要好几秒时间和几十兆内存!所以这个技巧只能用于真正需要的地方,比如with。虽然同样可以用于模拟Object.create(null)的行为,但是是否真的有这样的需求?或许更简单的方式是把一个普通对象上从Object.prototype继承来的属性遮蔽掉(即设为undefined),尽管这对于with来说不适合(原因请读者自己想),但是对于其他大多数情况可能就够用了。因此,我也不打算把这个trick提交给es5-shim项目,而只是仅用到我自己的一个项目中:
my.js。
以上。
分享到:
相关推荐
这个对象通常是一个“裸”对象,不具有任何属性或方法。在示例中,虽然没有显示创建空对象的代码,但实际过程会类似于`var obj = new Object();`这个新对象会成为接下来步骤中`this`关键字所指代的对象。 2. **绑定...
创建一个真正的HashMap,可以使用`Object.create(null)`来创建一个没有原型的对象,也称为“裸对象”或“字典”,这样可以避免原型链上的属性干扰: ```javascript var map = Object.create(null); map instanceof ...
创建了一个裸后端来演示使用 JavaScript 的身份验证和 JWT。 后端是使用NodeJS,Express,MongoDB创建的。 我还使用 Postman 来测试我的端点。 方法论/项目管理: 敏捷编码实践: OOP(面向对象编程) 编程语言/...
MSE允许开发者通过JavaScript向video元素注入媒体数据流,这样我们就可以构建一个包含H264数据的MediaSource对象,然后将其链接到video标签。 实现这个功能的步骤大致如下: 1. **服务器端**:使用WebSocket服务器...
为了创建一个更纯粹的Hash Map,可以使用`Object.create(null)`来创建一个没有原型的对象,即所谓的“裸对象”。这样的对象不会继承`Object.prototype`,因此避免了原型链带来的问题。例如: ```javascript var map...
开发者需要利用JavaScript动态创建MediaSource Extensions (MSE) 和SourceBuffer对象,将接收到的H264 NAL单元转换为可播放的Media Source,然后将其附加到`<video>`元素,实现流式播放。 6. **ActionA6T**:虽然这...
是的,另一个 Observable for Javascript 实现。 为什么是另一个 Observable? Javascript 有很多 Observable 的实现——为什么还有一个呢? 在某种程度上,作为在实际项目中学习 Javascript 的一部分。 因为我...
当你创建一个对象时,系统会分配内存空间。当对象不再被引用时,应该正确地释放这些内存,防止内存泄漏。Cocos2D-X使用了自动引用计数(ARC)来帮助开发者管理对象的生命周期。 2. 内存分配与释放:在C++中,内存...
查看fjs.cpp进行一个小示例,但实际上只是包含“ fjs.h”,然后您就可以访问JSParser对象。 实例化它,然后调用parse方法,该方法需要一个字符代码,然后您就可以离开! 还有一些方法可以注册c ++
【标题】"天乙社区修改版_bbscs7.zip"是一个包含Java JSP应用源码的压缩包,专为学生毕业设计学习而准备。这个压缩文件可能是某个开发者或团队为了帮助初学者理解Web应用程序开发,特别是Java Web技术,如JSP(Java...
WebSocket API是HTML5的一个重要特性,它为开发者提供了创建高性能、低延迟的实时Web应用的可能。传统HTTP协议不适合双向通信,每次请求都需要重新建立连接,而WebSocket则能在首次握手后保持连接,减少了网络开销,...
最后,数据存储在一个数组中并且始终可以访问。 用法 var target = [] var v1 = [1,2,3] var v2 = [4,5,6] v().add( v1, v2 ) // Returns the new array [5,7,9] v(target).add( v1, v2 ) // Sets the target to ...
裸对象安装一个 Node 加载器钩子,它转换每个加载的 JavaScript 文件并将<>语法扩展为Object.create(null) var foo = < > ; foo.bar = 'bar'; // use just like normal objects 因为裸对象不继承自 Object...
使用了裸对象datastore来进行元素存储; 实现了两种得到字典长度的方法,一种为变量跟踪,一种为实时计算。 代码: function(){ use strict; function Dictionary(){ this._size = 0; this.datastore = ...
《音乐美女404页面:倒计时跳转与自适应设计详解》 在互联网世界中,404页面是...而对于开发者和设计师而言,这个模板则提供了一个学习和参考的对象,启发他们如何将常见的错误提示页面转化为有趣且有用的互动空间。
2. **构造函数**:每个类都有一个特殊的构造函数,用于初始化新创建的对象。在ES6类中,构造函数使用`constructor`关键字定义。 3. **原型方法**:在ES6中,可以使用`prototype`属性来添加或修改类的原型方法,但...
奇热网作为一个知名的在线影视资源平台,其用户界面和用户体验都备受赞誉,因此这款模版的出现,旨在帮助开发者快速搭建一个类似奇热网的影视分享和观看网站。 在构建这样的网站时,我们需要考虑以下几个核心知识点...
SQL在Oracle中的运用是另一个关键知识点。SQL(Structured Query Language)是用于操作关系数据库的语言,Oracle支持标准SQL的同时,还提供了一些特有的语法和扩展,如PL/SQL,这是一种过程化编程语言,可以用于编写...
通过实例化类创建对象,对象之间可以通过方法进行交互。 5. 继承与多态:继承允许子类继承父类的属性和方法,实现代码复用。多态是面向对象的核心概念,通过抽象类和接口实现。 6. 枚举与结构:枚举是一种特殊的...
`h264.js`是一个JavaScript库,它的主要目的是通过JavaScript解析H264编码的数据,并将其转换成浏览器可以理解的格式,以便于利用`<video>`标签播放。这个库通常与`MediaSource Extensions`一起使用,`MSE`是HTML5...