contains方法:判定一个字符串是否包含另一个字符串。常规思维,使用正则,但每次都要用new RegExp来构造,太麻烦,性能太差。转而使用原生字符串方法。如indexOf , lastIdexOf , search
function contains (target, it) {
return target.lastIndexOf(it) != -1; //indexOf改成search, lastIndexOf也可以
}
在mootools版中,我们看到它支持更多的参数,目的是判定一个元素的className是否包含某个特定的class。众所周知,元素可以添加多个class,中间以空格隔开,使用mootoos的contains就很方便检测包含关系了。
function contains (target, str, separator) {
return separator ? ( separator + target + separator).indexOf(separator + str +separator) > -1 : target.indexOf(str) > -1;
}
repeat方法: 将一个字符串重复n次,如repeat("ruby",2)得到rubyruby
版本1:利用空数组的join方法
function repeat (target, n) {
return (new Array(n + 1)).join(target);
}
版本2.....6...
版本7,递归在浏览器下做了优化 ,包括ie6,属于最好的实现方式之一
function repeat (target, n) {
if (n == 1){
return target
}
var s = repeat(target, Math.floor(n/2));
s += s;
if (n % 2) {
s += target;
}
return s;
}
**byteLen方法:取得一个字符串所有字节的长度。**这是一个后端转过来的方法。在前端,我们要用户填写文本,需要字节有限制。
版本1:假设字符每个字符Unicode编码小于等于255,byteLength为字符串长度。再遍历字符串,遇到unicode编码大于255时,为byteLength加1
function byteLen (target) {
var byteLength = target.length,
i = 0;
for ( ; i < target.length ; i++) {
if (target.charCodeAt(i) > 255) {
byteLength++;
}
}
return byteLength;
}
truncate方法,用于对字符串进行截断处理,当超过限定长度,默认添加三个点号等
function truncate (target, length , truncation) {
length = length || 30;
truncation = truncation === void(0) ? '...' : truncation;
return target.length > length ? target.slice(0, length - truncation.length) + truncation : String(target);
}
camelize方法,转换为驼峰命名风格
function camelize (target) {
if (target.indexOf('_') < 0 && target.indexOf('_') < 0) {
return target; //提前判断,提高响应效率 }
return target.replace(/[-_][^-_]/g , function(match){
return match.charCodeAt(1).toUpperCase();
})
}
underscored方法。转换为下划线风格
function underscored(target){
return target.replace(/([a-z\d])([A-Z])/g , '$1_$2').replace(/\-/g , '_').toLowerCase();
}
dasherize方法,转换为连字符风格,亦指css变量风格(承上面的方法)
function dasherize(target){
return underscored(target).replace(/_/g, '-');
}
capitalize方法,首字母大写
function capitalize(target) {
return target.charAt(0).toUpperCase() + target.substring(1).toLowerCase();
}
stripTags方法,移除字符中的html标签。但有一个缺陷,如果其中有script标签,会把不该显示的脚本也显示出来。
function stripTags(target){
return String(target || "") .replace(/<[^>]+>/g, '');
}
escapeHTML和unescapeHTML略
escapeRegExp方法:将字符串安全格式转换为正则表达式的源码
function escapeRegExp(target){
return target.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
}
** pad方法**,与trim方法相反,pad可以为字符串的某一端添加字符串。常见的是在日历的月份前面加补0,因此,被称为fillZero。
function pad(target,n){
var zero = new Array(n).join('0');
var str = zero + target;
var resulet = str.substr(-n);
return resulet;
}
高级方法,也是mass Framework使用的版本,支持更多的参数。允许从左边或从右填充。以及使用什么内容进行填充。
function pad (target, n, filling, right, radix){
var num = target.toString(radix || 10);
filling = filling || "0";
while (num.length < n){
if(!right){
num = filling + num;
} else {
num += filling;
}
return num;
}
}
wbr方法,为目标字符串添加wbr换行。不过需要注意的是,它不是为每个字符串都插入
function wbr (target){
return String(target).replace(/(?:<[^>]+>) | (?:&#?[0-9a-z]{2,6};) | (.{1})/gi,'$&<wbr>').replace(/><wbr>/g,'>');
}
format方法,在c语言中,有一个printf方法,我们可以在后面添加不同的类型的参数嵌入到将要输出的字符串中。这是非常有用的方法,因为在javascript中涉及到大量这样字符串拼接的工作 ,如果涉及逻辑,我们可以用模板,如果轻量点,我们可以用这个方法。
在不同的框架中名字不同,prototype.js 叫interpolate,Base2叫format,mootools叫substitute。
function format (str, object){
var array = Array.prototype.slice.call(arguments, 1);
return str.replace(/\\?\#{([^{}]+)\}/gm,function(match, name) {
if(match.charAt(0) == '\\')
return match.slice(1);
var index = Number(name)
if(index >= 0)
return array[index];
if (object && object[name] !== void 0)
return object[name];
return '';
});
}
var a = format("resulet is #{0}, #{1}",22,33)
console.log(a) // resulet is 22, 33var b = format ( "#{name} is a #{sex} #{am}" ,{
name:"wangjie",
sex:"man",
am:"111"
});
console.log(b) // wangjie is a man 111
它支持两种传参方法,如果字符串的占位符为0,1,2这样的非零整数,要求传入两个或以上的参数,否则就传入一个对象,键名为占位符。
quote方法,在字符串的两端加上双引号。然后内部需要转义的地方都要转义。用于接装JSON的键名或模析系统中
//code.google.com/jQuery-jsonvar escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
meta = {
'\b':'\\b',
'\t':'\\t',
'\n':'\\n',
'\f':'\\f',
'\r':'\\r',
'"':'\\"',
'\\':'\\\\'
};
function quote(target){
if (target.match(escapeable)){
return '"' + target.replace(escapeable,function(a) {
var c = meta[a];
if(typeof c === 'string') {
return c;
}
return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)
}) + '"';
}
return '"' + target + '"';
}
当然,如果浏览器支持原生的JSON,我们直接用JSON.stringify就行了,另外,FF在JSON发明之前,就支持String.prototype.quote与String.quote方法了,我们使用quote之前判定浏览器是否内置这些方法
字符串好像没有打的浏览器兼容问题,有的话是IE6,IE7不支持用数组中括号取它的每一个字符,需要用charAt来取。IE678不支持垂直分表符,因此有如下hack
var isIe678 = !+"\v1";
修复旧版本IE中的trim函数。这是一个很常用的操作,通常我们需要把表单的两侧空白清除掉
版本1,用了两次正则,实际速度非常惊人,主要得益于浏览器的内部优化。base2使用这种优化,引起了其它浏览器的跟风。于是正则的实现再也比不过字符串方法了。一个著名的例子,字符串拼接。直接相加比Array做成的StringBuffer还快。而StringBuffer技术在早些年备受推崇。
function trim(str){
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
版本2
利用两个候选操作符链接两个正则,这样做就失去了浏览器优化的机会。由于看起来很优雅,很多类库都使用它。如jQuery,mootools
function trim(str){
return str.replace(/^\s+|\s+$/g, '');
}
版本3
更好的改进版
function trim(str){
var str = str.replace(/^\s\s*/, ""),
ws = /\s/,
i = str.length;
while (ws.test(str.charAt(--i)))
return str.slice(0, i + 1);
}
2.数组的扩展与修复
得益于Prototype.js的ruby式数组方法的侵略,让jser()的前端工程师大开眼界,原来对数组的操作是如此的丰富多彩。原来的javascript数组方法就是基于栈与队列的一套方法,像splice()还是很晚的时候加入的。让我们回顾一下用法。
**pop()**方法,出栈操作,删除并返回数组的最后一个元素
**push()**方法,出栈操作,向数组的末尾添加一个或更多元素,并返回新的长度。
**shift()**方法,出队操作,删除并返回数组的第一个元素
**unshift()**方法,入队操作,向数组的开头添加一个或更多的元素,返回新的长度
**slice()**方法,切片操作,从数组中分离出一个子数组,功能类似字符串的substring、slice、substr这三兄弟。此方法也常用于转换类数组对象为真正的数组
**sort()**方法,对数组元素进行排序,有一个可选参数,为比较函数。
**reverse()**方法,颠倒数组中元素的顺序。
**splice()**方法,用于用于同时对原数组进行增删操作,数组的remove方法就是基于它而写的
**concat()**方法,用于把原数组与参数合并成一个新数组,如果参数为数组,那么它会把其第一维的元素放入新的数组中。因此我们可以利用它实现数组的平坦化操作和克隆操作。
**join()**方法,把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。你可以想象成字符串的split的反操作。
在ecma262v5中,它把标准浏览器早已经实现的几个方法进行了入户处理。从此,我们可以安心的使用forEach()方法,不用担心他们被废弃掉了。
**indexOf()**方法,定位操作。同上,不是从后遍历。索引操作可以说是字符串的同名方法的翻版,存在就返回非负操作。不存在就返回-1.
**forEach()**方法。迭代操作。将数组的元素依次传入一个函数中执行,prototype.js对应的名字为 each。
**map()**方法,收集操作。将数组的元素依次传入一个函数中执行,然后把它们的返回值组成一个新数组返回。prototype.js对应的名字为collect.
**fliter()**方法。过滤操作,将数组的元素依次传入一个函数中执行,然后把返回值为true的那个元素放入新的数组中返回。prototype.js中它有三个名字,select、filter、findAll
**some()**方法,只要数组中有一个元素满足条件(放进函数返回true)。那么它就返回true.prototype.js对应的名字为any
**every()**方法,只有数组的元素满足调条件(放进给定函数返回true)它才返回true。prototype.js对应的名字为any
**reduce()**方法,归化操作。将数组中的元素归化为一个简单的数值。prototype.js对应的名字为inject
**reduceRight()**方法:归化操作,将数组中的元素归化为一个简单的数值。同上,不过是从后遍历。
由于许多扩展也基于这些新的标准方法,因此我们先给出IE678兼容方案,全部在数组原型上修复他们。
Array.prototype.indexOf = function(item, index){
var n = this.length, i = ~~index;
if(i<0)
i += n;
for(; i<n; i++)
if(this[i] === item)
return i;
return -1;
}
Array.prototype.lastIndexOf = function(ietm, index){
var n = this.length,
i = index == null ? n - 1 : index;
if (i < 0)
i = Math.max(0, n + 1);
for (; i >= 0; i--)
if (this[i] === item)
return i;
return -1;
}
像forEach map filter some every这几个方法,在结构上非常相似,我们可以这样生成他们。
function iterator (vars, body, ret){
var fun = 'for (var ' + vars + 'i = 0,n = this.length; i < n; i++ ){' +
body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))')
+ '}' + ret
return Function ("fn,scope", fun);
}
Array.prototype.forEach = iterator('','_','');
Array.prototype.filter = iterator('r=[],j=0', 'if(_)r[j++]=this[i]', 'return r');
Array.prototype.map = iterator('r=[],', 'r[i]=_' , 'return r');
Array.prototype.some = iterator('','if(_)return true', 'return false');
Array.prototype.every = iterator('','if(!_)return false','return true')
造轮子的同学注意:数组的空元素不会再上述方法中遍历出来。
[1,2,,4].forEach(function(e){
console.log(e)
})
接下来,我们将对数组的扩展进行总结,除去上述的
prototype.js的数组扩展:eachSlice , detect , grep , include , inGruopsOf , invoke, max, min, partition, pluck , reject, sortBy , zip , size, clear, first, last, compact, faltten , without, uniq , intersect, clone, inspect
Right.js 的数组扩展有 include, clean , clone , conpact, empty , first , flatten , includes, last, max , merge , min , random, reject, shuffle , size , sortBy, sum , uniq ,walk, without
mootools的数组扩展,clean, clone , associate , link , contains, append, getLast , getRandom, include, combine, erase, empty, flatten, min, max, mean, sum, erase, insert.
Ext的数组扩展, contains, pluck, clean, qunique, form , remove, include, merge,insersect, difference, flatten, min , max, mean, sum, erase, insert
Underscore.js 的数组扩展, detect, reject, invoke , pluck, sortBy, groupBy, sortedIndex, first, last, compact, flatten,without , union, intersection , difference, quiq, zip
qooxdoo数组扩展, insertAfter, insertAt, insertBefore , max , min , remove , removeAll , removeAt, sum , unique.
百度七巧板扩展, contains ,empty , find , remove , removeAt , unique
我们可以发现,Prototype.js 的一套方法影响深远, 许多库都有它的影子。我们可以根据需要与框架宗旨指定自己的数组扩展, 我们在这些方面考虑如下: 至少包含**:平坦化**,** 去重**, 乱序,移除这几个操作,其次是两个集合间的操作,如取并集。差集,交集。 下面是各种具体实现。
contains方法, 判定数组是否包含指定的目标
function contains(target, item){
return target.indexOf(item) > -1
}
removeAt方法,移除数组中指定位置的元素,返回布尔表示成功与否
function removeAt(target, index){
return !!target.splice(index, 1).length
}
remove方法,移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否。
function remove(target, ietm){
var index = target.indexOf(ietm);
if(~index)
return removeAt(target,index);
returnfalse;
}
**shuffle方法,对数组进行洗牌。**若不影响原数组,可以先拷贝一份出来操作,有关洗牌,可以参考 博文:http://bost.ocks.org/mike/shuffle/
function shuffle(target){
var j, x, i = target.length;
for(; i > 0; j = parseInt(Math.random() * i) ,
x = target[--i],target[i] = target[j] = x) {
}
return target
}
random方法,从数组中随机选出一个元素出来
function random(target){
return target[Math.floor(Math.random() * target.length)];
}
flatten方法,对数组进行平坦化处理,返回一个一维的新数组
function flatten(target){
var resulet = [];
target.forEach(function(item){
if (Array.isArray(item)) {
resulet = resulet.concat(flatten(item));
} else{
resulet.push(ietm)
}
});
return resulet;
}
unique方法,对数组去重,返回一个没有重复元素的新数组
function unique(target){
var resulet = [];
loop: for (var i = 0, n = target.length; i < n; i++){
for (var x = i + 1; x < n; x++){
if (target[x] === target[i])
continue loop;
}
resulet.push(target[i]);
}
return resulet;
}
compact方法,过滤数组中的null和undefined,但不影响数组。
function compact (target) {
return target.filter(function(el){
return el != null;
});
}
pluck方法,获得对象数组中的每个元素的指定属性,组成数组返回
function pluck(target, name){
var resulet = [], prop;
target.forEach(function(ietm){
prop = ietm[name];
if (prop != null)
resulet.push(prop);
});
return resulet;
}
groupBy方法:根据指定条件(如回调或对象的某个属性)进行分组,构成对象返回
function groupBy(target, val){
var resulet = {};
var iterator = $.isFunction(val) ? val : function (obj){
return obj[val];
};
target.forEach(function(value, index){
var key = iterator (value, index);
(resulet[key] || resulet[key] = [])).push(value);
});
return resulet
}
sortBy方法:根据指定条件进行排序,通常用于对象数组
function sortBy(target, fn, scope){
var array = target.map(function(ietm, index){
return {
el: ietm,
re: fn.call(scope, ietm, index)
};
}).sort(function(left, right){
var a = left.re, b = right.re;
return a < b ? -1 : a > b ? 1: 0;
})
return pluck(array, 'el');
}
union方法,对两个数组取并集
function union (target, array){
return unique(target.concat(array));
}
intersect方法:对两个数组取交集
function intersect(target, array){
return target.filter(function(n){
return ~array.indexOf(n);
});
}
diff方法,对两个数组取差集
function diff(target, array){
var resulet = target.slice();
for (var i = 0, i < resulet.length; j++) {
for (var j = 0; j < resulet.length; j++) {
if (resulet[i] === array[j]){
resulet.splice(i, 1);
i--;
break;
}
}
}
return resulet;
}
min方法,返回数组的最小值,用于数字数组
function min (target){
return Math.min.apply(0, target);
}
max方法,返回数组中的最大值。用于数字数组
function max(target){
return Math.max.apply(0, target);
}
** 3.数值的扩展与修复**
数值没有什么好扩展的,而且javascript的数值精度问题一向臭名昭著,修复不是一两行代码的事情。先看扩展。
Prototype.js为它添加了8个原型方法,Succ是加1,times是将回调重复执行制定次数,toPaddingString与上面提到的字符串扩展方法pad作用一样,toColorPart是转十六机制,abs,ceil,floor,是从Math中提取出来的
mootools的情况,limit是从数值限在一个闭开间中,如果大于或小于其边界,则等于其最大值或最小值,times与prototype.js用法相似,round是Math.round的增强版,添加了精度控制,toFloat,toInt是从window中偷学来的。
看看mass Framework对数字的扩展
limit方法,确保数值在[n1,n2]闭区间之内,如果超出界限,则置换为离它最近的最大值或最小值。
function limit(target, n1, n2){
var a = [n1, n2].sort();
if (target < a[0])
target = a[0];
if(target > a[1])
target = a[1];
return target;
}
nearer方法,求出距离数值最近的那个数。
function nearer(target, n1, n2){
var diff1 = Math.abs(target - n1),
diff2 = Math.abs(target - n2);
return diff1 < diff2 ? n1 : n2
}
Number下唯一要修复的方法是toFixed,它是用来校正精确度,最后的那个数会四舍五入操作,但在一些浏览器中,并未这么操作。 简单修复可以这样处理
if(0.9.toFixed(0) !== '1') {
Number.Prototype.toFixed = function(n) {
var power = Math.pow(10, n);
var fixed = (Math.round(this*power)/power).toString();
if(n == 0)
return fixed;
if(fixed.indexOf('.') < 0)
fixed += '.';
var padding = n + 1 (fixed.length - fixed.indexOf('.'));
for (var i = 0; i < padding; i++)
fixed += '0';
return fixed;
}
}
关于javascript误差运算不在这里呈现了,但在工作中,我们尽量避免小数操作与大数操作,或者转交后台处理,实在避免不了,可以引入专业的库来实现。
4.函数的扩展与修复
V5对函数的唯一扩展就是bind函数,众所周知,这来自prototype.js,此外,其它重要的函数都来自prototype.js
prototype.js的函数扩展如下:
argumentNames:取得函数的形参,以字符串形式返回,这只要用于其类工厂的方法链设计
bind,不用多描述,劫持作用域,并预先添加更多的参数。
bindAsEventListener 如bind相似,但强制返回函数的第一个参数为事件对象,这是用于修复IE多投事件API与标准API的差异。
curry 函数柯里化,用于一个操作分成多步进行,并可以改变原函数的行为。
wrap:AOP 的实现。
delay:setTimeout的偷懒写法。
defer:强制延迟0.01秒才执行原函数
methodize:将一个函数变成其调用对象的方法,这也是为其类工厂的方法服务。
首先我们看bind方法,它用到了著名的闭包(所谓闭包,就是引用着外部变量的内部函数),比如下面这个函数
[
](javascript:void(0);)var observable = function(val){
var cur = val; //一个内部变量function field(neo) {
if (arguments.length){ //setterif (cur !== neo) {
cur = neo
}
} else { //getterreturn cur;
}
}
field();
return field;
}
它里边的field函数将于外部的cur构成一个闭包。prototype.js中的bind方法只要依仗原来=函数与经过切片话的args构成闭包,而这个方法就是名副其实的curry柯里化,用户最初的那个传参,劫持到返回函数修正this的指向。
Function.Prototype.bind = function(context) {
if (arguments.length < 2 && context == void 0)
returnthis;
var _method = this, args = [].slice.call(arguments, 1);
returnfunction() {
return _method.apply(context, args.context.apply(args, arguments));
}
}
因为有这个原型扩展,我们才可以修复ie的多投事件API attachEvent回到的this问题,它总是指向window对象,而标准的浏览器的的addEventListener中的this则其调用对象
var addEvent = document.addEventListener ?
function (el, type, fn, capture){
el.addEventListener (type, fn, capture)
} :
function (el, type, fn) {
el.attachEvent("on" + type, fn.bind(el, event))
}
ECMA62 V5对其认证以后,唯一的增强是对调用者进行监测,确保它是一个函数。顺便总结一下这三个东西。
call是obj.method(a, b, c)到method(obj,[a, b, c])的变换,它要求第二个参数必须存在,一定是数组或Arguments这样的类数组,NodeList这样具有争议性的东西就不能传入进去! 因此,jQuery对两个数组或类数组的合并就使用jQuery.merge,放弃使用Array.prototype.push.apply。
bind就是apply的变种,保证返回值是一个函数。
这三个方法非常有用,我们设法将其还原出来。
var bind = function(bind) {
return {
bind: bind.bind(bind),
call: bind.bind(bind.call),
apply: bind.bind(bind.apply)
}
} (Function.Prototype.bind)
那么怎么使用它们呢,比如我们想合并两个数组,直接调用concat方法:
var concat = bind.apply([].concat);
var a = [1, [2,3], 4];
var b = [1, 3];
使用bind.bind方法可以将它们的结果进一步平坦化
var concat = bind.apply([].concat);
console.log(concat(b,a)); //=> [1,3,1,2,3,4]
又如切片化操作,它经常用于转换类数组对象为纯数组
var slice = bind([].slice)
var array = slice({
0: "aaa",
1: "bbb",
2: "ccc",
3: "ddd"
length: 4
});
console.log(array) //=> ["aaa","bbb","ccc","ddd"]
更常用的操作是转换arguments对象,目的是为了使用数组的一系列方法
function test() {
var args = slice(arguments)
console.log(args)//=> [1,2,3,4,5] }
test(1, 2, 3, 4, 5)
我们可以将hasOwnProperty提取出来,判定对象是否在本地就拥有某属性
var hasOwn = bind.call(Object.Prototype.hasOwnProperty);
hasOwn([],"xx") //false//使用bind.bind就需要多执行一次var hasOwn2 = bind.bind(Object.Prototype.hasOwnProperty);
hasOwn2([],"xx")() //false
上面的bind.bind的行为就是一种curry,它给了你一种传参的机会,这样你就可以在内部判定参数的个数,决定是否继续返回函数还是结果。这在设计计算器的连续运算上非常有用。从此角度,我们可以得到一条信息,bind着重作用域的劫持,curry在于参数的不断补充。
我们可以编写一个curry,当所有步骤输入的参数个数等于最初定义时的函数形参个数,就执行它。
function curry(fn) {
function inner(len, arg) {
if (len == 0)
return fn.apply(null, arg);
returnfunction(x) {
return inner(len - 1, arg.concat(x));
};
}
return inner(fn.length, []);
}
function sum(x, y, z, w){
return x + y + z + w;
}
curry(sum)('a')('b')('c')('d'); //=> 'abcd'
不过这里我们假定了用户每次都只传入一个参数,我们可以改进下
function curry2(fn) {
function inner (len, arg){
if (len <= 0)
return fn.apply(null, arg);
returnfunction() {
return inner (len - arguments.length,
arg.concat(Array.apply([],arguments)));
}
}
return inner(fn.length, []);
}
这样,我们就可以在中途传递多个参数,或不传递参数
curry2(sum)('a')('b','c')('d'); //=> 'abcd'
curry2(sum)('a')()('b','c')()('d'); //=> 'abcd'
不过,上面的函数形式有个更帅气的名称,叫self-curry,或recurry.它强调的是递归自身来补全参数。 与curry相似的partial。curry的不足 参数总是通过push的方式来补全,而partial则是在定义时所有参数都已经有了,但某些位置上的参数值是个占位符,我们在接下来的传参只是替换掉它们。博客上专门有《partial application in javascript》来介绍这个内容。
Function.Prototype.partial = function(){
var fn = this, args = Array.prototype.slice.call(arguments);
returnfunction() {
var arg = 0;
for (var i = 0 ; i < args.length && arg < arguments.length; i++)
if (args[i] === undefined)
args[i] = arguments[args++];
return fn.apply(this, args);
}
}
它是使用undefined作为占位符的。
var delay = setTimeout.partial(undefined, 10);
//接下来的工作就代替掉第一个参数
delay(function() {
console.log("A call to this function will be temporarily delayed")
})
curry、partial应用场景在前端世界应用的并不多,前端讲究的是即时显示,许多API都是同步的,后端由于IO操作等耗时够长,像node.js提供了大量的异步函数来提高性能,防止堵塞。但是过多的异步函数必然带来回调嵌套的问题。因此,我们需要curry等函数变换。将嵌套减少到可以接受的程度。在ajax中会讲述他们的使用办法。
**函数的修复,这涉及到方法,apply和call,这两个方法的本质就是生成一个新的函数。**将原函数与用于的传参放到里面执行而已,在javascript中创造一个函数有很多办法,常见的有函数声明和函数表达式,次之是函数构造器,再次之是eval,setTimeout....
5.日期的扩展与修复
Date构造器是javascript中传参最丰富的构造器。大致分为四种:
new Date();
new Date(value); //传入毫秒鼠new Date(dateString)
new Date(year, month, day/*, hour, minute, second, minllisecond*/)
其中,第三种可以玩N多花样,个人建议只使用"2009/07/12 12:34:56",后面的分秒可以省略。这个所有的浏览器都支持,此构造器的兼容列表可见此文。
http://dygraphs.com/date-formats.html
若要修正它的传参,可能是个庞大的工程。并且影响Object.prototype.toString的类型的判断。因此,不建议修正。es5.js中的源码,可以参考
https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js
javascript的日期是抄自java.util.Date,但是Date这个类的很多方法对时区等支持不够,且不少都过时,java程序员也推荐使用calnedar类代替Date类。javascript可以选择余地比较小。如对属性使用了前后矛盾的偏移量。月份与小时都是基于0.月份中的天数则是基于1,而年则是从1900开始的.
接下来,我们为旧版的浏览器添加几个ecma262标准化日期方法吧
if(!Date.now) {
Date.now = function(){
return + new Date;
}
}
if (!Date.prototype.toISOString) {
voidfunction() {
function pad (number) {
var r = String(number);
if (r.length === 1) {
r = '0' + r ;
}
return r
}
Date.prototype.toJSON = Date.prototype.toISOString = function() {
returnthis.getUTCFllYear()
+ '_' + pad(this.getUTCMonth() + 1)
+ '_' + pad(this.getUTCDate())
+ 'T' + pad(this.getUTCHours())
+ ':' + pad(this.getUTCMinutes())
+ ':' + pad(this.getUTCSeconds())
+ '.' + String((this.getUTCMilliseconds()/1000).toFixed(3)).clice(2, 5)
+ 'Z';
}
}();
}
IE67 中,getYear, setYear方法存在bug.这个修起来比较简单:
if ((new Date).getYear() > 1900){
Date.prototype.getYear = function(){
returnthis.getFullYear() - 1900;
};
Date.prototype.setYear = function(year){
returnthis.setFullYear(year); //+1900 };
}
至于扩展,由于涉及本地化的原因,外国许多日期库都需要改一改才能用。其中以dataFormat这个很有用的方法为最。先给一些常用的扩展。
传入两个Date类型的日期,求其相隔的天数
var getDatePeriod = function(start, finish){
return Math.abs(start * 1 - finish * 1) / 60 / 60 / 1000 / 24;
}
传入一个Date类型的日期,判断它所在月的第一天。
var getFirstDateInMouth = function(date) {
returnnew Date(Date.getFullYear(), Date.getMonth(), 1);
}
传入一个Date类型的日期,判断它所在月的最后一天。
var getLastDateInMouth = function(date) {
returnnew Date(Date.getFullYear(), Date.getMonth() + 1, 0);
}
判断是否是闰年
Date.prototype.isLeapYear = function(){
returnnew Date(this.getFullYear(),2 ,0).getDate() == 29;
}
获得当前月份的天数
function getDaysInMotn1(date) {
switch (Date.getMonth()) {
case 0:
case 2:
case 4:
case 6:
case 7:
case 9:
case 11:
return 31;
case 1:
var y = Date.getFullYear();
return y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
default :
return 30;
}
}
function getDaysInMonth2(date) {
returnnew Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
}
dateFormat方法太长,此处略。
 
POST和GET的区别,HTTP状态码
301与302:二者都是进行重定向,前者为永久重定向,后者为临时重定向。301,302对用户来说没有区别,他们看到效果只是一个跳转,浏览器中旧的URL变成了新的URL。实际工作中,当我们的前一个域名被永久性停止使用,并且不希望用户还能访问以前的域名时,我们会用到301。
www.cnblogs.com/milo-wjh/p/…
www.cnblogs.com/blogcxz/p/7…
CommonJS,AMD,CMD,ES6
commonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。
CMD是另一种js模块化方案,它与AMD很类似,不同点在于:AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。此规范其实是在sea.js推广过程中产生的。
/ AMD写法 / define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { // 等于在最前面声明并初始化了要用到的所有模块 a.doSomething(); if (false) { // 即便没用到某个模块 b,但 b 还是提前执行了 b.doSomething() } });
/ CMD写法 / define(function(require, exports, module) { var a = require('./a'); //在需要时申明 a.doSomething(); if (false) { var b = require('./b'); b.doSomething(); } });
ES6 模块是动态引用:ES6的模块不是对象,import命令会被 JavaScript 引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。
ES6 模块与 CommonJS 模块的差异
1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用 CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。 ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。 2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。 编译时加载: ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
大数相加 http://www.cnblogs.com/kindofblue/p/4672129.html
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
setTimeout(fn, 0) 的作用
深拷贝浅拷贝
Element.matches 精确匹配
Document
每个在浏览器中加载的页面都有一个
document
对象。通过document
对象可以访问页面的内容(比如dom树,和内部的元素)document
对象可以由以下几种方法获取所有的
document
对象都是通过Document
接口实现的PropertiesDocument.body
返回当前文档的
<body>
或<frameset>
元素,如果元素不存在则返回null例(在页面加载完成后执行):
给文档设置一个新的body需要先删除已经存在的
<body>
元素Document.documentElement
它是一个只读属性,返回文档的根节点元素(例如对于html文档来说,返回的是
<html>
元素)例:
Notes:
Document.forms
返回当前文档中form元素的集合(一个HTMLCollection对象)
例:
Document.head
返回当前文档的
<head>
元素,如果文档中大于一个<head>
元素,返回第一个元素。Notes:
Document.images
返回当前文档image元素的集合
例子:
Notes:
Document.scripts
返回文档的script元素列表(是一个HTMLCollection对象)
例子:
Document.title
获取或设置文档的标题
例子:
Document.anchors
返回文档中的锚点列表
例子:
Notes:
Document.links
links属性返回文档中带
href
属性的<area>
元素和<a>
元素的集合Document.location
Document.location
是一个只读属性,返回一个Location
对象,它包含了当前文档URL的信息以及提供了一些方法来改变URL地址或者是加载另一个URLDocument.URL
返回当前文档的url,但不能设置url
Document.referrer
返回链接到当前文档的url(之前link过来的路径)
Document.domain
获取或设置文档的域名
Document.activeElement
返回当前文档焦点所在的元素
Example
注:当文档加载完成后,
document.activeElement
返回document.body
,在文档加载期间document.activeElement
是null.Document.readyState
当文档正在加载时返回"loading",解析完成但仍然加载资源返回"interactive",所有的资源全部加载完成返回"complete"。
readystatechange事件当readyState属性改变时触发。
Document.cookie
设置或读取当前文档的cookie
读取所有cookie:
allCookies = document.cookie;
读取后的cookie是一个键值对的字符串设置新的cookie:
document.cookie = newCookie;
Document.defaultView
返回文档对应的window对象
MethodsDocument.getElementById()
通过元素id返回元素的引用,如果在文档中找不到id,返回null(id是大小写敏感的)
Document.getElementsByTagName()
根据标签名返回元素的集合(一个HTMLCollection对象)
Document.getElementsByName()
根据节点的属性name值返回元素的集合(一个NodeList对象)
Example
Document.getElementsByClassName()
根据类名返回元素的集合(一个类数组的对象HTMLCollection),当作用在document上,将搜索整个文档的。也可以通过
element.getElementsByClassName()
作用在指定的节点元素上查找。Example1
Example2
Document.querySelector()
通过匹配css选择器,返回第一个匹配的元素。
Example
Document.querySelectorAll()
通过匹配css选择器返回NodeList对象
Example1
Example2 返回div元素带有calss为'note'或'alert'元素的集合
Document.createElement()
创建一个html元素
Document.createTextNode()
创建一个文本节点
Document.createAttribute()
创建一个属性节点
Example
初始html为:
现在html为:
Document.hasFocus()
指示文档或文档中的任何元素是否处于焦点状态。这个方法用来检查在文档中是否有焦点元素
Example
Document.createDocumentFragment()
创建一个空的文档片段
多次使用节点方法(如:appendChild)绘制页面,每次都要刷新页面一次。效率也就大打折扣了,而使用document.createDocumentFragment()创建一个文档碎片,把所有的新结点附加在其上,然后把文档碎片的内容一次性添加到document中,这也就只需要一次页面刷新就可
Js 数组——filter()、map()、some()、every()、forEach()、lastIndexOf()、indexOf()
filter():
语法:
var filteredArray = array.filter(callback[, thisObject]);
参数说明:
callback: 要对每个数组元素执行的回调函数。 thisObject : 在执行回调函数时定义的this对象。
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
功能说明:
对数组中的每个元素都执行一次指定的函数(callback),并且创建一个新的数组,该数组元素是所有回调函数执行时返回值为 true 的原数组元素。它只对数组中的非空元素执行指定的函数,没有赋值或者已经删除的元素将被忽略,同时,新创建的数组也不会包含这些元素。
回调函数可以有三个参数:当前元素,当前元素的索引和当前的数组对象。
如参数 thisObject 被传递进来,它将被当做回调函数(callback)内部的 this 对象,如果没有传递或者为null,那么将会使用全局对象。
filter 不会改变原有数组,记住:只有在回调函数执行前传入的数组元素才有效,在回调函数开始执行后才添加的元素将被忽略,而在回调函数开始执行到最后一个元素这一期间,数组元素被删除或者被更改的,将以回调函数访问到该元素的时间为准,被删除的元素将被忽略。
map():
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
some():
对数组中的每个元素都执行一次指定的函数(callback),直到此函数返回 true,如果发现这个元素,some 将返回 true,如果回调函数对每个元素执行后都返回 false ,some 将返回 false。它只对数组中的非空元素执行指定的函数,没有赋值或者已经删除的元素将被忽略。
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
every():
对数组中的每个元素都执行一次指定的函数(callback),直到此函数返回 false,如果发现这个元素,every 将返回 false,如果回调函数对每个元素执行后都返回 true ,every 将返回 true。它只对数组中的非空元素执行指定的函数,没有赋值或者已经删除的元素将被忽略
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
forEach():
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
lastIndexOf():
语法
参数说明
searchElement: 要搜索的元素
fromIndex : 开始搜索的位置,默认为数组的长度(length),在这样的情况下,将搜索所有的数组元素。搜索是反方向进行的。
功能说明
比较 searchElement 和数组的每个元素是否绝对一致(===),当有元素符合条件时,返回当前元素的索引。如果没有发现,就直接返回 -1 。
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
indexOf():
功能与lastIndexOf()一样,搜索是正向进行的
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
JavaScript中提供了多种数组方法,如下:
转换方法:
具体看一下例子:
栈方法:
队列方法:
具体看一下例子:
作者: 关于郑州想的都是你
链接:www.imooc.com/article/167…
来源:慕课网
一、题目
用JS代码求出页面上一个元素的最终的background-color,不考虑IE浏览器,不考虑元素float情况。
二、题目解析
1.考察底层JavaScript基础 前端开发,日常最常接触的就是页面样式的编写。而摆脱jQuery等工具库,用原生js获取样式,是每个前端程序猿进阶阶段必须掌握的技能。
2.考察面试者的思维缜密程度和开发经验 如果认为单单求元素计算后的样式,就有点too young了。页面的样式的复杂,永远是最虐心的。就算前端有多牛逼,一听到兼容IE6,论谁都会心塞😓。所以还要考虑特殊的情况:
display,opacity,visibility
的取值。三、理论基础
1. 内联样式 内联样式可以通过元素的style属性获取,如果style属性有background-color值,则可以直接获取出来 (暂不考虑!important) 。
2. 外联的层叠样式 DOM2样式规范在
document.defaultView
中包含了一个getComputedStyle()
方法。该方法返回一个只读的CSSStyleDeclaration对象,其中包含特定元素的所有计算样式。四、解题
4.1 将所有工具方法封装在WDS(wall dom script)命名空间中
代码封装在命名空间里,不会造成无意间的代码污染。
4.2 工具方法camelize
该方法是为了方便后续
getStyle()
方法的编写,而独立出来的。 作用是将连字符类的css属性值,转换成驼峰写法。 例如:将background-color转换为backgroundColor4.3 获取特定元素的计算样式
做到这一步,第一个考察点基本就满足了。也能获知面试者是否具备足够扎实的js基础。 另外,像安全保护性的判断
if(!elem || !property)
和功能嗅探if(document.defaultView && document.defaultView.getComputedStyle)
,都能很好地体现开发者的代码逻辑和开发经验。4.4 排除特殊情况
4.5 获取div在页面最终显示的颜色
获取样式值采用递归方式处理。 如果能顺利获取到元素样式,且不触发
4.4 排除特殊情况
中的一种,则直接返回结果。 触发了特殊情况,则需要查找父节点以及更上层的节点的样式,来获取肉眼能看到,显示在页面上的background-color值。 在向上回溯的过程中,如果已经回溯到html
根节点,则可以停止回溯。所以加了判断else if(elem != document.documentElement)
五、遗漏的大boss
5.1 大boss !important 如果乱用
!important
,对大型项目的维护和开发,绝对是一场噩梦。因为优先级规则的计算,!important
永远处在食物链的最顶层。 当前题目不考虑这种情况,也是我的偷懒😆。确实很棘手,就不写这个逻辑分支的代码了。这里提醒一下~5.2 大boss 父节点及根节点设置了不可见css属性 只要设置该css语句:
html {display:none;}
,页面所有元素立刻消失不见。而任意特定元素的上级节点,只要设置了opacity,display,visibility
,判断逻辑瞬间变得复杂起来。所以,这个浑水我也不趟 O(∩_∩)O哈哈~六、改进的点
其实特殊情况排除的判断,我偷懒没做到最好——rgb颜色值和特定颜色值(比如red)没有进行统一的转换,只是加了生硬的判断
if(value == "transparent" || value == "rgba(0, 0, 0, 0)")
。 有兴趣的可以搜索下颜色值转换的js方法,这里我就不写了。七、源码和demo
源码地址:
[github.com/wall-wxk/bl…](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fwall-wxk%2FblogDemo%2Fblob%2Fmaster%2F2017%2F02%2F05%2FgetStyle.html)
demo:[wall-wxk.github.io/blogDemo/20…](https://link.juejin.im?target=https%3A%2F%2Fwall-wxk.github.io%2FblogDemo%2F2017%2F02%2F05%2FgetStyle.html)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
补充
谢谢@烟雨雾岚 的提醒的新思路:canvas截图+Js获取图片某点颜色,这样可以完美解决所有的问题。
一、值
1)数字
JavaScript只有一种数值类型:number(数字),包括“整数”和带小数的十进制数。
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
由于数字值可以使用Number对象进行封装,因此数字值可以调用Number.prototype中的方法。例如,tofixed(..)方法可指定小数部分的显示位数:
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
2)整数检测
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
3)null与undefined
特殊数值undefined与null。它们的名称既是类型也是值。
null指空值(empty value),曾赋过值,但是目前没有值。null是一个特殊关键字,不是标识符,我们不能将其当作变量来使用和赋值。
undefined指没有值(missing value),从未赋值。undefined是一个标识符,可以被当作变量来使用和赋值。
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
4)不是数字的数字
NaN意指“不是一个数字”(not a number),将它理解为“无效数值”、“失败数值”或者“坏数值”可能更准确些。
NaN是一个特殊值,它和自身不相等,是唯一一个非自反(自反,即x === x不成立)的值。而NaN != NaN为true。
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
5)零值
加法和减法运算不会得到负零(negative zero)。负零在开发调试控制台中通常显示为“-0”,但在一些老版本的浏览器中仍然会显示为“0”。
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
6)特殊等式
NaN和-0在相等比较时的表现有些特别。
由于NaN和自身不相等,所以必须使用ES6中的Number.isNaN(..)。 而-0等于0(对于===也是如此),因此我们必须使用isNegZero(..)这样的工具函数。
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
7)值和引用
JavaScript引用指向的是值。如果一个值有10个引用,这些引用指向的都是同一个值,它们相互之间没有引用/指向关系。
向函数传递值的时候,实际是将引用值的一个复本传递进去,不管是基本类型还是对象。
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
上面的源码可以在此次浏览。
二、原生函数
常用的原生函数有:String()、Number()、Boolean()、Array()、Object()、Function()、RegExp()、Date()、Error()、Symbol()。
1)内部属性[[Class]]
这个属性无法直接访问,一般通过Object.prototype.toString(..)来查看。
虽然Null()和Undefined()这样的原生构造函数并不存在,但是内部[[Class]]属性值仍然是"Null"和"Undefined"。
基本类型值被各自的封装对象自动包装,所以它们的内部[[Class]]属性值分别为"String"、"Number"和"Boolean"。
2)封装对象包装
由于基本类型值没有.length和.toString()这样的属性和方法,需要通过封装对象才能访问,此时JavaScript会自动为基本类型值包装(box或者wrap)一个封装对象:
如果想要自行封装基本类型值,可以使用 Object(..) 函数(不带 new 关键字)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
3)拆封
如果想要得到封装对象中的基本类型值,可以使用valueOf()函数:
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
在需要用到封装对象中的基本类型值的地方会发生隐式拆封。