Skip to content

Instantly share code, notes, and snippets.

@asins
Last active February 25, 2019 14:32
Show Gist options
  • Save asins/9701dc86d884aeb20883 to your computer and use it in GitHub Desktop.
Save asins/9701dc86d884aeb20883 to your computer and use it in GitHub Desktop.
IE中getter/setter 的实现
<!DOCTYPE html>
<html>
<head>
<title>A Crazy Getter/Setter Hack</title>
</head>
<body>
<ul>
<li>Object.defineProperty API:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty</li>
<li>https://github.com/RubyLouvre/avalon/blob/master/avalon.shim.js#L1394</li>
<li>https://gist.github.com/jeffrafter/189354</li>
<li>http://download.dojotoolkit.org/release-1.9.7/dojo-release-1.9.7/dojox/lang/observable.js.uncompressed.js</li>
</ul>
<pre>
通过vbscript来hack的方式勉强可用,不过有以下缺点:(https://github.com/migijs/migi/issues/5#issuecomment-103778075)
1. vbscript是不区分大小写的
2. vbscript不能增删属性,所以每次需重新定义class,实现上麻烦且bug,且无法确保如delete之类的语义
3. 性能糟糕
4. 很大的内存泄漏风险(因为vbscript的垃圾回收机制是计数)
</pre>
<script type="text/javascript">
var defineProperty = Object.defineProperty
var canHideOwn = true
//如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8
//标准浏览器使用__defineGetter__, __defineSetter__实现
try {
defineProperty({}, "_", {
value: "x"
})
var defineProperties = Object.defineProperties
} catch (e) {
canHideOwn = false
}
if (!canHideOwn) {
if ("__defineGetter__" in avalon) {
defineProperty = function (obj, prop, desc) {
if ('value' in desc) {
obj[prop] = desc.value
}
if ("get" in desc) {
obj.__defineGetter__(prop, desc.get)
}
if ('set' in desc) {
obj.__defineSetter__(prop, desc.set)
}
return obj
}
defineProperties = function (obj, descs) {
for (var prop in descs) {
if (descs.hasOwnProperty(prop)) {
defineProperty(obj, prop, descs[prop])
}
}
return obj
}
}
if (IEVersion) {
window.execScript([ // jshint ignore:line
"Function parseVB(code)",
"\tExecuteGlobal(code)",
"End Function",
"Dim VBClassBodies",
"Set VBClassBodies=CreateObject(\"Scripting.Dictionary\")",
"Function findOrDefineVBClass(name,body)",
"\tDim found",
"\tfound=\"\"",
"\tFor Each key in VBClassBodies",
"\t\tIf body=VBClassBodies.Item(key) Then",
"\t\t\tfound=key",
"\t\t\tExit For",
"\t\tEnd If",
"\tnext",
"\tIf found=\"\" Then",
"\t\tparseVB(\"Class \" + name + body)",
"\t\tVBClassBodies.Add name, body",
"\t\tfound=name",
"\tEnd If",
"\tfindOrDefineVBClass=found",
"End Function"
].join("\n"), "VBScript")
function VBMediator(instance, accessors, name, value) {// jshint ignore:line
var accessor = accessors[name]
if (arguments.length === 4) {
accessor.call(instance, value)
} else {
return accessor.call(instance)
}
}
defineProperties = function (name, accessors, properties) {
var className = "VBClass" + setTimeout("1"),// jshint ignore:line
buffer = []
buffer.push(
"\r\n\tPrivate [__data__], [__proxy__]",
"\tPublic Default Function [__const__](d, p)",
"\t\tSet [__data__] = d: set [__proxy__] = p",
"\t\tSet [__const__] = Me", //链式调用
"\tEnd Function")
//添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好
for (name in properties) {
if (!accessors.hasOwnProperty(name)) {
buffer.push("\tPublic [" + name + "]")
}
}
$$skipArray.forEach(function (name) {
if (!accessors.hasOwnProperty(name)) {
buffer.push("\tPublic [" + name + "]")
}
})
buffer.push("\tPublic [" + 'hasOwnProperty' + "]")
//添加访问器属性
for (name in accessors) {
buffer.push(
//由于不知对方会传入什么,因此set, let都用上
"\tPublic Property Let [" + name + "](val" + expose + ")", //setter
"\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")",
"\tEnd Property",
"\tPublic Property Set [" + name + "](val" + expose + ")", //setter
"\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")",
"\tEnd Property",
"\tPublic Property Get [" + name + "]", //getter
"\tOn Error Resume Next", //必须优先使用set语句,否则它会误将数组当字符串返回
"\t\tSet[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")",
"\tIf Err.Number <> 0 Then",
"\t\t[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")",
"\tEnd If",
"\tOn Error Goto 0",
"\tEnd Property")
}
buffer.push("End Class")
var code = buffer.join("\r\n"),
realClassName = window['findOrDefineVBClass'](className, code) //如果该VB类已定义,返回类名。否则用className创建一个新类。
if (realClassName === className) {
window.parseVB([
"Function " + className + "Factory(a, b)", //创建实例并传入两个关键的参数
"\tDim o",
"\tSet o = (New " + className + ")(a, b)",
"\tSet " + className + "Factory = o",
"End Function"
].join("\r\n"))
}
var ret = window[realClassName + "Factory"](accessors, VBMediator) //得到其产品
return ret //得到其产品
}
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment