Polyfill源码阅读
自己作为前端大半年的新手,刚刚红皮书看完,开始看一些比较浅层次的源码,来加深对于规范的理解。看polyfill顺便可以看一下js的兼容,还可以了解一些不常用的API,这里记录了自己一些阅读所得,欢迎大神指教。
CSSOM
getBoundingClientRect这个方法在ie8下的修正
if ('TextRectangle' in this && !('width' in TextRectangle.prototype)) {
Object.defineProperties(TextRectangle.prototype, {
'width': { get: function() { return this.right - this.left; } },
'height': { get: function() { return this.bottom - this.top; } }
});
}
getBoundingClientRect这个方法得到这个元素的size并且相对于视图的定位,返回的几个值都是只读
的
这里对getBoundingClientRect方法在ie8下面没有width和height进行了休整。就是对ie8 window下的TextRectangle进行了defineProperties。将width和height进行了定义(这里的定义之所以使用defineProperties是因为这里定义了访问器属性,这个属性不能直接定义)。注意在新的浏览器里面,TextRectangle已经改名了。
注意这里使用的defineProperties在ie 8以下并不支持,所以这个polyfill已经对这个方法进行了修整。就是循环调用了ie8支持的defineProperty而已,至于ie7以下不支持的defineProperty,会在下面进行分析。
Object.prototype.hasOwnProperty.call(properties, name)
这里通过hasOwnProperty这个判断来只遍历properties里面的属性,而不是原型对象上的属性;使用call来调用是为了防止对象上自己申明了这个方法,这样子可以使用原型上的。
var s = {};
s === Object({}) //true
这里通过Object方法来判断传入的是不是一个object~这个写法有点意思。
Dom
querySelectorAll
document.querySelectorAll = function(selectors) {
var style = document.createElement('style'), elements = [], element;
document.documentElement.firstChild.appendChild(style);
document._qsa = [];
style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
window.scrollBy(0, 0);
style.parentNode.removeChild(style);
while (document._qsa.length) {
element = document._qsa.shift();
element.style.removeAttribute('x-qsa');
elements.push(element);
}
document._qsa = null;
return elements;
};
看的好开心,感觉实现很有意思,通过支持率较好的css选择器来做这件事情
就是新建一个style的元素,并且使用传入的字段作为选择器,然后自定义了一个x-qsa的属性。使用到了expression这个东西来调用js,就是应用到的元素自身推入一个准备好的数组中。
然后遍历数组,得到最后的结果了,大赞!!
写法虽然不错,但是css的expression ie8以后不再支持了,比较尴尬,但是用这个来兼容ie7及以下还是很有意思的。
querySelector
就是调用querySelectorAll,然后返回第一个值就行了
getElementsByClassName
类似的,调用上面的querySelectorAll,只要在前面替换个.就行了
Node 类型
因为ie没有公布它的Node Type。所以这里添加了Node.ELEMENT_NODE这些。
DOMException
与上面类似的,定义了DOMException的一些种类
Event
重写了ie8以下的一些事件处理,包括通过attachevent来模拟addEventListener,通过detachEvent来模拟removeEventListener,这些ie9才支持
[Window, HTMLDocument, Element].forEach(function(o) {
o.prototype.addEventListener = addEventListener;
o.prototype.removeEventListener = removeEventListener;
});
重写了这几个方法之后对window和element应用。
DOMTokenList
这个东西就是一个接口,一个格式,元素的classList,rellist就是遵从的这个规范,就是以空格分开的一些字符串而已。这里就是为IE9以下重写了这个东西,提供了一个构造函数,并且使用defineProperty来提供那些toggle,add等等的方法。
es5
getPrototypeOf
这个方法IE8以下不支持。
实现就是在判断了obj !== Object(obj)
也就是返回obj.__proto__ || obj.constructor.prototype || Object.prototype
,proto这个属性是一些浏览器自己的实现(google,safari,ff)的,不推荐使用。
注意这个方法在ES6下的shim比较复杂,因为在ES6下,一个字符串会返回String.prototype
,而es5则会报错,不过这个方法支持的很好。参见ES6支持
getOwnPropertyNames
IE8以下不支持。
这个就是返回所有属于他自己的属性,实现就是在for in中运行一次Object.prototype.hasOwnProperty就可以了。
这个polyfill有一些问题,不能cover下面的,参见mdn文档规范
var arr = ['a', 'b', 'c'];
console.log(Object.getOwnPropertyNames(arr).sort()); // logs '0,1,2,length'
因为他的写法是通过for in循环的,而length在array中是不可枚举的,作者表示除了原生的getOwnPropertyNames,其他没法拿到不可枚举的。
Object.create
Object.create = function (prototype, properties) {
if (typeof prototype !== "object") { throw TypeError(); }
function Ctor() {}
Ctor.prototype = prototype;
var o = new Ctor();
if (prototype) { o.constructor = Ctor; }
if (properties !== undefined) {
if (properties !== Object(properties)) { throw TypeError(); }
Object.defineProperties(o, properties);
}
return o;
};
第一个参数是原型,第二个参数是property,才开始还以为这里比mdn文档网站上的polyfill写的好。因为他敢用defineProperties,结果后来才发现他也就支持了get,set和value。
mdn上的直接循环赋值了第二个参数,所以那些getter,setter,writable,enumerable属性没法设定了,这里鸡贼的调用了defineProperties,后面他的实现也只有getter和setter。
这里把constructor赋值回去的做法已经被放弃了,浏览器都已经放弃这一步操作了....
Object.defineProperty
(function() {
if (!Object.defineProperty ||
!(function () { try { Object.defineProperty({}, 'x', {}); return true; } catch (e) { return false; } } ())) {
var orig = Object.defineProperty;
Object.defineProperty = function (o, prop, desc) {
// In IE8 try built-in implementation for defining properties on DOM prototypes.
if (orig) { try { return orig(o, prop, desc); } catch (e) {} }
if (o !== Object(o)) { throw TypeError("Object.defineProperty called on non-object"); }
if (Object.prototype.__defineGetter__ && ('get' in desc)) {
Object.prototype.__defineGetter__.call(o, prop, desc.get);
}
if (Object.prototype.__defineSetter__ && ('set' in desc)) {
Object.prototype.__defineSetter__.call(o, prop, desc.set);
}
if ('value' in desc) {
o[prop] = desc.value;
}
return o;
};
}
}());
因为IE8部分支持了这个方法(只支持DOM Object),所以这里的检测调用了一次,并用了个try catch
来返回值。
这里在能够使用IE8自带的情况下返回自带的。
然后就是使用了两个已经被废弃的函数。__defineGetter__
和__defineSetter__
来操作访问器属性,但是那些enumerable,writable就不行了。
Object.defineProperties
这里就是对传入的properties进行for in,然后用hasOwnProperty检测一下。再调用上面的Object.defineProperty。
先写到这里,顺便给个github的传送门,喜欢的朋友star一下啊,自己平时遇到的问题,以及学习所得在上面都会进行记录~