这是蚂蚁面试官推荐我读的一个项目的源码,可能因为代码结构比较简单,主要解决兼容性,对于前端小白来说比较有用吧。
Object.getPrototypeOf ( O )
该静态方法用来返回参数的原型对象。
if (!Object.getPrototypeOf) {
Object.getPrototypeOf = function (o) {
if (o !== Object(o)) { throw TypeError("Object.getPrototypeOf called on non-object"); }
return o.__proto__ || o.constructor.prototype || Object.prototype;
};
}
以上是polyfill的实现,_proto_这个属性是一些浏览器自己的实现,不建议使用。我发现了一点美中不足:对于Object.create(null)的纯净对象(没有原型的)出现了意外的报错!就想瞎改一下。
return o.__proto__ || (o.constructor) ? (o.constructor.prototype || Object.prototype) : null;
Object.getOwnPropertyNames ( O )
该静态方法用来返回参数自身(非原型链)的属性名数组。
if (typeof Object.getOwnPropertyNames !== "function") {
Object.getOwnPropertyNames = function (o) {
if (o !== Object(o)) { throw TypeError("Object.getOwnPropertyNames called on non-object"); }
var props = [], p;
for (p in o) {
if (Object.prototype.hasOwnProperty.call(o, p)) {
props.push(p);
}
}
return props;
};
}
一次for-in遍历对象,hasOwnProperty验证对应属性是不是非原型链上,思考过keys方法多好(也不完全一样),兼容性和这个方法完全一样,也就没有意义了。美中不足:只能取出可枚举属性。
Object.create ( O [, Properties] )
创建对象,自定义它的原型和自带属性(支持用描述符)。
if (typeof Object.create !== "function") {
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;
};
}
内部创建了一个构造函数,写了一个原型模式。o.constructor = Ctor;这个我就不太懂了。constructor这个属性本来就慎用(除非我们了解它的机制,就是初始的松散的对应关系),我觉得指向Object比这样好。
Object.defineProperty( O, P, Attributes )
该方法用来配置对象的属性描述符。
(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;
};
}
}());
为什么是一个自运行函数呢?orig! 通过_defineGetter_和_defineSetter_设置get和set方法,value自然赋值就可以,可惜无法设置其他描述符。
Object.defineProperties ( O, Properties )
批量进行Object.defineProperty操作
if (typeof Object.defineProperties !== "function") {
Object.defineProperties = function (o, properties) {
if (o !== Object(o)) { throw TypeError("Object.defineProperties called on non-object"); }
var name;
for (name in properties) {
if (Object.prototype.hasOwnProperty.call(properties, name)) {
Object.defineProperty(o, name, properties[name]);
}
}
return o;
};
}
if (Object.prototype.hasOwnProperty.call(properties, name))可以规避原型链上的属性。
Object.keys ( O )
返回参数对象自身可枚举属性名数组。
if (!Object.keys) {
Object.keys = function (o) {
if (o !== Object(o)) { throw TypeError('Object.keys called on non-object'); }
var ret = [], p;
for (p in o) {
if (Object.prototype.hasOwnProperty.call(o, p)) {
ret.push(p);
}
}
return ret;
};
}
对比Object.getOwnPropertyNames发现实现的方法一样,也是无奈。
Function.prototype.bind ( thisArg [, arg1 [, arg2, ... ]] )
这个方法可以用来拷贝函数,函数柯里化,绑定this。。。。。。
if (!Function.prototype.bind) {
Function.prototype.bind = function (o) {
if (typeof this !== 'function') { throw TypeError("Bind must be called on a function"); }
var args = Array.prototype.slice.call(arguments, 1),
self = this,
nop = function() {},
bound = function () {
return self.apply(this instanceof nop ? this : o,
args.concat(Array.prototype.slice.call(arguments)));
};
if (this.prototype)
nop.prototype = this.prototype;
bound.prototype = new nop();
return bound;
};
}
个人觉得args.concat(Array.prototype.slice.call(arguments)))还挺精妙的,参数拼接再apply调用,而且还做了函数原型链的继承,自己写就考虑不到。
Array.isArray ( arg )
顾名思义,判断参数是不是数组。
Array.isArray = Array.isArray || function (o) { return Boolean(o && Object.prototype.toString.call(Object(o)) === '[object Array]'); };
意料之外,还有这种类型检查的方法,大概是利用了,Array、Function等从Object托生出来之后重写了toString,调用老的toString。function也可以如此判断。
Array.prototype.indexOf ( searchElement [ , fromIndex ] )
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */) {
if (this === void 0 || this === null) { throw TypeError(); }
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) { return -1; }
var n = 0;
if (arguments.length > 0) {
n = Number(arguments[1]);
if (isNaN(n)) {
n = 0;
} else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) { return -1; }
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
};
}
- void 我还以为我看见了C语言,其实js是有void函数的,可以把void 0当成undefined(好处有二:void 0小三个字节;避免非保留字undefined被赋值的情况。)
- t.length >>> 0,开发小白平时鲜有用到位操作,但是位操作真的是利器。无符号右移主要做了什么呢?首先要强转Number类型,转不了置0,然后非整数变整数,最后如果是负数,返回负数 + 2的32次方。
- var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); 负数则从后标志索引位。
还有就是一直在做参数类型的转化,感觉polyfill对这样一个依靠遍历的常用方法在性能上花了心思。
Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
if (!Array.prototype.lastIndexOf) {
Array.prototype.lastIndexOf = function (searchElement /*, fromIndex*/) {
if (this === void 0 || this === null) { throw TypeError(); }
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) { return -1; }
var n = len;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n !== n) {
n = 0;
} else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);
for (; k >= 0; k--) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
};
}
感觉如出一辙。。。。。。
Array.prototype.every ( callbackfn [ , thisArg ] )
if (!Array.prototype.every) {
Array.prototype.every = function (fun /*, thisp */) {
if (this === void 0 || this === null) { throw TypeError(); }
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== "function") { throw TypeError(); }
var thisp = arguments[1], i;
for (i = 0; i < len; i++) {
if (i in t && !fun.call(thisp, t[i], i, t)) {
return false;
}
}
return true;
};
}
延续前面的风格,看来polyfill对数组遍历的处理方法是一致的。
some、forEach、map、filter、reduce、reduceRight(这两个区别比前几个大一点)不想赘述了
String.prototype.trim()
删除字符串两边的空格。
if (!String.prototype.trim) {
String.prototype.trim = function () {
return String(this).replace(/^\s+/, '').replace(/\s+$/, '');
};
}
正则匹配,去前空格,再去后空格就可以了。
Date.now ( )
方法返回自1970年1月1日 00:00:00 UTC到当前时间的毫秒数。
if (!Date.now) {
Date.now = function now() {
return Number(new Date());
};
// 这个有多种写法
/*
Date.now1 = function now() {
return new Date().getTime();
};
Date.now2 = function now() {
return Number(new Date());
};
Date.now3 = function now() {
return new Date().valueOf();
};
*/
}
Date.prototype.toISOString ( )
方法返回一个 ISO格式的字符串: YYYY-MM-DDTHH:mm:ss.sssZ。时区总是UTC(协调世界时),加一个后缀“Z”标识。
if (!Date.prototype.toISOString) {
Date.prototype.toISOString = function () {
function pad2(n) { return ('00' + n).slice(-2); }
function pad3(n) { return ('000' + n).slice(-3); }
return this.getUTCFullYear() + '-' +
pad2(this.getUTCMonth() + 1) + '-' +
pad2(this.getUTCDate()) + 'T' +
pad2(this.getUTCHours()) + ':' +
pad2(this.getUTCMinutes()) + ':' +
pad2(this.getUTCSeconds()) + '.' +
pad3(this.getUTCMilliseconds()) + 'Z';
};
}