Last active
February 25, 2019 14:32
-
-
Save asins/9701dc86d884aeb20883 to your computer and use it in GitHub Desktop.
IE中getter/setter 的实现
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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