Polyfill源碼閱讀
自己作為前端大半年的新手,剛剛紅皮書看完,開始看一些比較淺層次的源碼,來加深對(duì)于規(guī)范的理解。看polyfill順便可以看一下js的兼容,還可以了解一些不常用的API,這里記錄了自己一些閱讀所得,歡迎大神指教。
CSSOM
getBoundingClientRect這個(gè)方法在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這個(gè)方法得到這個(gè)元素的size并且相對(duì)于視圖的定位,返回的幾個(gè)值都是只讀的
這里對(duì)getBoundingClientRect方法在ie8下面沒有width和height進(jìn)行了休整。就是對(duì)ie8 window下的TextRectangle進(jìn)行了defineProperties。將width和height進(jìn)行了定義(這里的定義之所以使用defineProperties是因?yàn)檫@里定義了訪問器屬性,這個(gè)屬性不能直接定義)。注意在新的瀏覽器里面,TextRectangle已經(jīng)改名了。
注意這里使用的defineProperties在ie 8以下并不支持,所以這個(gè)polyfill已經(jīng)對(duì)這個(gè)方法進(jìn)行了修整。就是循環(huán)調(diào)用了ie8支持的defineProperty而已,至于ie7以下不支持的defineProperty,會(huì)在下面進(jìn)行分析。
Object.prototype.hasOwnProperty.call(properties, name)
這里通過hasOwnProperty這個(gè)判斷來只遍歷properties里面的屬性,而不是原型對(duì)象上的屬性;使用call來調(diào)用是為了防止對(duì)象上自己申明了這個(gè)方法,這樣子可以使用原型上的。
var s = {};
s === Object({}) //true
這里通過Object方法來判斷傳入的是不是一個(gè)object~這個(gè)寫法有點(diǎn)意思。
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;
};
看的好開心,感覺實(shí)現(xiàn)很有意思,通過支持率較好的css選擇器來做這件事情
就是新建一個(gè)style的元素,并且使用傳入的字段作為選擇器,然后自定義了一個(gè)x-qsa的屬性。使用到了expression這個(gè)東西來調(diào)用js,就是應(yīng)用到的元素自身推入一個(gè)準(zhǔn)備好的數(shù)組中。
然后遍歷數(shù)組,得到最后的結(jié)果了,大贊?。?/p>
寫法雖然不錯(cuò),但是css的expression ie8以后不再支持了,比較尷尬,但是用這個(gè)來兼容ie7及以下還是很有意思的。
querySelector
就是調(diào)用querySelectorAll,然后返回第一個(gè)值就行了
getElementsByClassName
類似的,調(diào)用上面的querySelectorAll,只要在前面替換個(gè).就行了
Node 類型
因?yàn)閕e沒有公布它的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;
});
重寫了這幾個(gè)方法之后對(duì)window和element應(yīng)用。
DOMTokenList
這個(gè)東西就是一個(gè)接口,一個(gè)格式,元素的classList,rellist就是遵從的這個(gè)規(guī)范,就是以空格分開的一些字符串而已。這里就是為IE9以下重寫了這個(gè)東西,提供了一個(gè)構(gòu)造函數(shù),并且使用defineProperty來提供那些toggle,add等等的方法。
es5
getPrototypeOf
這個(gè)方法IE8以下不支持。
實(shí)現(xiàn)就是在判斷了obj !== Object(obj)也就是返回obj.__proto__ || obj.constructor.prototype || Object.prototype,proto這個(gè)屬性是一些瀏覽器自己的實(shí)現(xiàn)(google,safari,ff)的,不推薦使用。
注意這個(gè)方法在ES6下的shim比較復(fù)雜,因?yàn)樵贓S6下,一個(gè)字符串會(huì)返回String.prototype,而es5則會(huì)報(bào)錯(cuò),不過這個(gè)方法支持的很好。參見ES6支持
getOwnPropertyNames
IE8以下不支持。
這個(gè)就是返回所有屬于他自己的屬性,實(shí)現(xiàn)就是在for in中運(yùn)行一次Object.prototype.hasOwnProperty就可以了。
這個(gè)polyfill有一些問題,不能cover下面的,參見mdn文檔規(guī)范
var arr = ['a', 'b', 'c'];
console.log(Object.getOwnPropertyNames(arr).sort()); // logs '0,1,2,length'
因?yàn)樗膶懛ㄊ峭ㄟ^for in循環(huán)的,而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;
};
第一個(gè)參數(shù)是原型,第二個(gè)參數(shù)是property,才開始還以為這里比mdn文檔網(wǎng)站上的polyfill寫的好。因?yàn)樗矣胐efineProperties,結(jié)果后來才發(fā)現(xiàn)他也就支持了get,set和value。
mdn上的直接循環(huán)賦值了第二個(gè)參數(shù),所以那些getter,setter,writable,enumerable屬性沒法設(shè)定了,這里雞賊的調(diào)用了defineProperties,后面他的實(shí)現(xiàn)也只有g(shù)etter和setter。
這里把constructor賦值回去的做法已經(jīng)被放棄了,瀏覽器都已經(jīng)放棄這一步操作了....
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;
};
}
}());
因?yàn)镮E8部分支持了這個(gè)方法(只支持DOM Object),所以這里的檢測(cè)調(diào)用了一次,并用了個(gè)try catch來返回值。
這里在能夠使用IE8自帶的情況下返回自帶的。
然后就是使用了兩個(gè)已經(jīng)被廢棄的函數(shù)。__defineGetter__和__defineSetter__來操作訪問器屬性,但是那些enumerable,writable就不行了。
Object.defineProperties
這里就是對(duì)傳入的properties進(jìn)行for in,然后用hasOwnProperty檢測(cè)一下。再調(diào)用上面的Object.defineProperty。
先寫到這里,順便給個(gè)github的傳送門,喜歡的朋友star一下啊,自己平時(shí)遇到的問題,以及學(xué)習(xí)所得在上面都會(huì)進(jìn)行記錄~