对于想了解将自定义函数添加到Array.prototype的读者,本文将是一篇不可错过的文章,我们将详细介绍可以将自定义函数名作为参数传递给另一个函数,并且为您提供关于Array,prototype.
对于想了解将自定义函数添加到Array.prototype的读者,本文将是一篇不可错过的文章,我们将详细介绍可以将自定义函数名作为参数传递给另一个函数,并且为您提供关于Array,prototype.toString.call()、Array.prototype.concat()、Array.prototype.copyWithin()、Array.prototype.every()的有价值信息。
本文目录一览:- 将自定义函数添加到Array.prototype(可以将自定义函数名作为参数传递给另一个函数)
- Array,prototype.toString.call()
- Array.prototype.concat()
- Array.prototype.copyWithin()
- Array.prototype.every()
将自定义函数添加到Array.prototype(可以将自定义函数名作为参数传递给另一个函数)
我正在研究启用AJAX的asp.net应用程序。我刚刚向Array.prototype添加了一些方法,例如
Array.prototype.doSomething = function(){ ...}
该解决方案对我有用,可以以“漂亮”的方式重用代码。
但是,当我测试了它与整个页面一起使用时,我遇到了问题。我们有了一些自定义的Ajax扩展程序,它们开始表现出意想不到的效果:某些控件在其内容或值上显示为“未定义”。
这可能是什么原因?我是否缺少修改标准对象原型的东西?
注意:我很确定,当我为Array修改原型时,错误就开始了。它应该仅与IE兼容。
答案1
小编典典通常,修改内置对象原型不是一个好主意,因为它总是有可能与其他供应商或加载在同一页面上的库的代码发生冲突。
在Array对象原型的情况下,这是一个特别糟糕的主意,因为它有可能干扰在任何数组的成员上迭代的任何代码段,例如使用for .. in
。
为了举例说明:
Array.prototype.foo = 1;// somewhere deep in other javascript code...var a = [1,2,3,4,5];for (x in a){ // Now foo is a part of EVERY array and // will show up here as a value of ''x''}
不幸的是,这样做的可疑代码的存在使得有必要也避免使用简单for..in
的数组迭代,至少如果您想要最大的可移植性,只是为了防止其他一些令人讨厌的代码修改了Array原型的情况。因此,您确实需要同时做这两个事情:for..in
如果某些n00b修改了Array原型,则应避免使用plain,并且应避免修改Array原型,以免弄乱任何使用plainfor..in
来遍历数组的代码。
您最好创建自己的带有doSomething函数的对象构造函数类型,而不是扩展内置Array。
那Object.defineProperty
呢
现在,存在Object.defineProperty
一种扩展对象原型的通用方法,而无法枚举新属性,尽管这仍然不能证明扩展 内置
类型的合理性,因为即使如此for..in
,它仍然可能与其他脚本发生冲突。考虑使用两个Javascript框架的人,它们都试图以相似的方式扩展Array并选择相同的方法名称。或者,考虑有人分叉
您的 代码,然后将原始版本和分叉版本都放在同一页面上。Array对象的自定义增强功能是否仍然有效?
这是Javascript的现实,也是为什么即使使用,也应该避免修改内置类型的原型的原因Object.defineProperty
。用自己的构造函数定义自己的类型。
Array,prototype.toString.call()
在 JavaScript 中,想要判断某个对象值属于哪种内置类型,最靠谱的做法就是通过 Object.prototype.toString 方法.
1
2
|
var
arr = [];
console.log(Object.prototype.toString.call(arr))
//"[object Array]"
|
本文要讲的就是,toString 方法是如何做到这一点的,原理是什么.
ECMAScript 3
在 ES3 中,Object.prototype.toString 方法的规范如下:
1
|
15.2.4.2 Object.prototype.toString()
|
在 toString 方法被调用时,会执行下面的操作步骤:
1. 获取 this 对象的 [[Class]] 属性的值.
2. 计算出三个字符串 "[object", 第一步的操作结果 Result (1), 以及 "]" 连接后的新字符串.
3. 返回第二步的操作结果 Result (2).
[[Class]] 是一个内部属性,所有的对象 (原生对象和宿主对象) 都拥有该属性。在规范中,[[Class]] 是这么定义的
内部属性 | 描述 |
---|---|
[[Class]] | 一个字符串值,表明了该对象的类型. |
然后给了一段解释:
所有内置对象的 [[Class]] 属性的值是由本规范定义的。所有宿主对象的 [[Class]] 属性的值可以是任意值,甚至可以是内置对象使用过的 [[Class]] 属性的值.[[Class]] 属性的值可以用来判断一个原生对象属于哪种内置类型。需要注意的是,除了通过 Object.prototype.toString 方法之外,本规范没有提供任何其他方式来让程序访问该属性的值 (查看 15.2.4.2).
也就是说,把 Object.prototype.toString 方法返回的字符串,去掉前面固定的 "[object" 和后面固定的 "]", 就是内部属性 [[class]] 的值,也就达到了判断对象类型的目的.jQuery 中的工具方法 $.type (), 就是干这个的.
在 ES3 中,规范文档并没有总结出 [[class]] 内部属性一共有几种,不过我们可以自己统计一下,原生对象的 [[class]] 内部属性的值一共有 10 种。分别是:"Array", "Boolean", "Date", "Error", "Function", "Math", "Number", "Object", "RegExp", "String".
ECMAScript 5
在 ES5.1 中,除了规范写的更详细一些以外,Object.prototype.toString 方法和 [[class]] 内部属性的定义上也有一些变化,Object.prototype.toString 方法的规范如下:
15.2.4.2 Object.prototype.toString ( )
在 toString 方法被调用时,会执行下面的操作步骤:
如果 this 的值为 undefined, 则返回 "[object Undefined]".
如果 this 的值为 null, 则返回 "[object Null]".
让 O 成为调用 ToObject (this) 的结果.
让 class 成为 O 的内部属性 [[Class]] 的值.
返回三个字符串 "[object", class, 以及 "]" 连接后的新字符串.
可以看出,比 ES3 多了 1,2,3 步。第 1,2 步属于新规则,比较特殊,因为 "Undefined" 和 "Null" 并不属于 [[class]] 属性的值,需要注意的是,这里和严格模式无关 (大部分函数在严格模式下,this 的值才会保持 undefined 或 null, 非严格模式下会自动成为全局对象). 第 3 步并不算是新规则,因为在 ES3 的引擎中,也都会在这一步将三种原始值类型转换成对应的包装对象,只是规范中没写出来.ES5 中,[[Class]] 属性的解释更加详细:
所有内置对象的 [[Class]] 属性的值是由本规范定义的。所有宿主对象的 [[Class]] 属性的值可以是除了 "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String" 之外的的任何字符串.[[Class]] 内部属性是引擎内部用来判断一个对象属于哪种类型的值的。需要注意的是,除了通过 Object.prototype.toString 方法之外,本规范没有提供任何其他方式来让程序访问该属性的值 (查看 15.2.4.2).
和 ES3 对比一下,第一个差别就是 [[class]] 内部属性的值多了两种,成了 12 种,一种是 arguments 对象的 [[class]] 成了 "Arguments", 而不是以前的 "Object", 还有就是多个了全局对象 JSON, 它的 [[class]] 值为 "JSON". 第二个差别就是,宿主对象的 [[class]] 内部属性的值,不能和这 12 种值冲突,不过在支持 ES3 的浏览器中,貌似也没有发现哪些宿主对象故意使用那 10 个值.
ECMAScript 6
ES6 目前还只是工作草案,但能够肯定的是,[[class]] 内部属性没有了,取而代之的是另外一个内部属性 [[NativeBrand]].[[NativeBrand]] 属性是这么定义的:
内部属性 | 属性值 | 描述 |
---|---|---|
[[NativeBrand]] | 枚举 NativeBrand 的一个成员. | 该属性的值对应一个标志值 (tag value), 可以用来区分原生对象的类型. |
[[NativeBrand]] 属性的解释:
[[NativeBrand]] 内部属性用来识别某个原生对象是否为符合本规范的某一种特定类型的对象.[[NativeBrand]] 内部属性的值为下面这些枚举类型的值中的一个:NativeFunction, NativeArray, StringWrapper, BooleanWrapper, NumberWrapper, NativeMath, NativeDate, NativeRegExp, NativeError, NativeJSON, NativeArguments, NativePrivateName.[[NativeBrand]] 内部属性仅用来区分区分特定类型的 ECMAScript 原生对象。只有在表 10 中明确指出的对象类型才有 [[NativeBrand]] 内部属性.
表 10 — [[NativeBrand]] 内部属性的值
属性值 | 对应类型 |
---|---|
NativeFunction | Function objects |
NativeArray | Array objects |
StringWrapper | String objects |
BooleanWrapper | Boolean objects |
NumberWrapper | Number objects |
NativeMath | The Math object |
NativeDate | Date objects |
NativeRegExp | RegExp objects |
NativeError | Error objects |
NativeJSON | The JSON object |
NativeArguments | Arguments objects |
NativePrivateName | Private Name objects |
可见,和 [[class]] 不同的是,并不是每个对象都拥有 [[NativeBrand]]. 同时,Object.prototype.toString 方法的规范也改成了下面这样:
15.2.4.2 Object.prototype.toString ( )
在 toString 方法被调用时,会执行下面的操作步骤:
如果 this 的值为 undefined, 则返回 "[object Undefined]".
如果 this 的值为 null, 则返回 "[object Null]".
让 O 成为调用 ToObject (this) 的结果.
如果 O 有 [[NativeBrand]] 内部属性,让 tag 成为表 29 中对应的值.
否则
让 hasTag 成为调用 O 的 [[HasProperty]] 内部方法后的结果,参数为 @@toStringTag.
如果 hasTag 为 false, 则让 tag 为 "Object".
否则,
让 tag 成为调用 O 的 [[Get]] 内部方法后的结果,参数为 @@toStringTag.
如果 tag 是一个 abrupt completion, 则让 tag 成为 NormalCompletion ("???").
让 tag 成为 tag.[[value]].
如果 Type (tag) 不是字符串,则让 tag 成为 "???".
如果 tag 的值为 "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", 或
者 "String" 中的任一个,则让 tag 成为字符串 "~" 和 tag 当前的值连接后的结果.
返回三个字符串 "[object", tag, and "]" 连接后的新字符串.
表 29 — [[NativeBrand]] 标志值
[[NativeBrand]] 值 | 标志值 |
---|---|
NativeFunction | "Function" |
NativeArray | "Array" |
StringWrapper | "String" |
BooleanWrapper | "Boolean" |
NumberWrapper | "Number" |
NativeMath | "Math" |
NativeDate | "Date" |
NativeRegExp | "RegExp" |
NativeError | "Error" |
NativeJSON | "JSON" |
NativeArguments | "Arguments" |
可以看到,在规范上有了很大的变化,不过对于普通用户来说,貌似感觉不到.
也许你发现了,ES6 里的新类型 Map,Set 等,都没有在表 29 中。它们在执行 toString 方法的时候返回的是什么?
1
2
|
console.log(Object.prototype.toString.call(Map()))
//"[object Map]"
console.log(Object.prototype.toString.call(Set()))
//"[object Set]"
|
其中的字符串 "Map" 是怎么来的呢:
15.14.5.13 Map.prototype.@@toStringTag
@@toStringTag 属性的初始值为字符串 "Map".
由于 ES6 的规范还在制定中,各种相关规定都有可能改变,所以如果想了解更多细节。看看下面这两个链接,现在只需要知道的是:[[class]] 没了,使用了更复杂的机制.
以上所述是小编给大家分享的 JavaScript 中 Object.prototype.toString 方法的原理,希望对大家有所帮助!
Array.prototype.concat()
concat () 方法用于合并两个或多个数组。
此方法不改变原数组,而是返回一个新数组。
参数是数组或者值,返回值是新数组。
concat 方法不会改变 this 或任何作为参数提供的数组,而是返回一个浅拷贝,因此要注意引用类型的数据如果其属性改变,对于原数组和新数组都是可见的。
var alpha = [''a'', ''b'', ''c''];
var numeric = [1, 2, 3];
alpha.concat(numeric);
// result in [''a'', ''b'', ''c'', 1, 2, 3]
var num1 = [1, 2, 3],
num2 = [4, 5, 6],
num3 = [7, 8, 9];
var nums = num1.concat(num2, num3);
console.log(nums);
// results in [1, 2, 3, 4, 5, 6, 7, 8, 9]
var alpha = [''a'', ''b'', ''c''];
var alphaNumeric = alpha.concat(1, [2, 3]);
console.log(alphaNumeric);
// results in [''a'', ''b'', ''c'', 1, 2, 3]
var num1 = [[1]];
var num2 = [2, [3]];
var nums = num1.concat(num2);
console.log(nums);
// results in [[1], 2, [3]]
// modify the first element of num1
num1[0].push(4);
console.log(nums);
// results in [[1, 4], 2, [3]]
自己实现简单的 concat ():
Array.prototype.concat = function () {
var res = [];
res.push.apply(res, this);
for (var i = 0; i < arguments.length; i++) {
if (Object.prototype.toString.call(arguments[i]) == ''[object Array]'') {
res.push.apply(res, arguments[i]);
} else {
res.push.call(res, arguments[i]);
}
}
return res;
}
Array.prototype.copyWithin()
copyWithin()方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,而不修改其大小。
此方法会改变数组自身。
这是一个ES6方法。
target
复制序列到该位置。如果是负数,target 将从末尾开始计算。
如果 target 大于等于 arr.length,将会不发生拷贝。如果 target 在 start 之后,复制的序列将被修改以符合 arr.length。
start
开始复制元素的起始位置。如果是负数,start 将从末尾开始计算。
如果 start 被忽略,copyWithin 将会从0开始复制。
end
开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算。
如果 end 被忽略,copyWithin 将会复制到 arr.length。
copyWithin()不会改变 this 的 length,但是会改变 this 本身的内容,且需要时会创建新的属性。
[1, 2, 3, 4, 5].copyWithin(-2);
// [1, 2, 3, 1, 2]
[1, 2, 3, 4, 5].copyWithin(0, 3);
// [4, 5, 3, 4, 5]
[1, 2, 3, 4, 5].copyWithin(0, 3, 4);
// [4, 2, 3, 4, 5]
[1, 2, 3, 4, 5].copyWithin(-2, -3, -1);
// [1, 2, 3, 3, 4]
[].copyWithin.call({length: 5, 3: 1}, 0, 3);
// {0: 1, 3: 1, length: 5}
// ES2015 Typed Arrays are subclasses of Array
var i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// On platforms that are not yet ES2015 compliant:
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]
polyfill
if (!Array.prototype.copyWithin) {
Array.prototype.copyWithin = function(target, start/*, end*/) {
// Steps 1-2.
if (this == null) {//如果this是null,抛错误
throw new TypeError(''this is null or not defined'');
}
var O = Object(this);//将this转换成对象
// Steps 3-5.
var len = O.length >>> 0;//???不知为何要无符号右移0位,可能是避免object类型的length是undefined
// Steps 6-8.
var relativeTarget = target >> 0;//复制到的目标位置,右移0位,做取整,相当于parseInt(Number(target) || 0)
var to = relativeTarget < 0 ?
Math.max(len + relativeTarget, 0) :
Math.min(relativeTarget, len);//处理target是负数的情况
// Steps 9-11.
var relativeStart = start >> 0;//复制内容的起始位置,右移0位,取整
var from = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);//处理start是负数的情况
// Steps 12-14.
var end = arguments[2];//复制内容的结束位置
var relativeEnd = end === undefined ? len : end >> 0;//如果end不存在就是this.length
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);//处理end是负数的情况
// Step 15.
var count = Math.min(final - from, len - to);//Math.min(end - start, len - target),最终需要覆盖的元素个数
// Steps 16-17.
var direction = 1;
if (from < to && to < (from + count)) {//(start < target && target < (start + count))
//如果target和复制范围有重叠,那么就从尾部往前赋值,方向相反,从右向左,避免重叠部分还要存临时变量
direction = -1;
from += count - 1;
to += count - 1;
}
// Step 18.
while (count > 0) {//循环赋值
if (from in O) {
O[to] = O[from];
} else {
delete O[to];
}
from += direction;
to += direction;
count--;
}
// Step 19.
return O;
};
}
Array.prototype.every()
function isBigEnough(element, index, array) {
return (element >= 10);
}
var passed = [12, 5, 8, 130, 44].every(isBigEnough);
// passed is false
passed = [12, 54, 18, 130, 44].every(isBigEnough);
// passed is true
if (!Array.prototype.every)
{
Array.prototype.every = function(fun /*, thisArg */)
{
''use strict'';
if (this === void 0 || this === null)
throw new TypeError();//如果数组等于undefined就抛出错误
var t = Object(this);//数组转成对象
var len = t.length >>> 0;//数组长度
if (typeof fun !== ''function'')
throw new TypeError();//fun不是函数就抛出错误
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;//第二个参数this
for (var i = 0; i < len; i++)
{
if (i in t && !fun.call(thisArg, t[i], i, t))
return false;//如果fun函数返回值有一个是false就返回false
}
return true;
};
}
今天关于将自定义函数添加到Array.prototype和可以将自定义函数名作为参数传递给另一个函数的介绍到此结束,谢谢您的阅读,有关Array,prototype.toString.call()、Array.prototype.concat()、Array.prototype.copyWithin()、Array.prototype.every()等更多相关知识的信息可以在本站进行查询。
本文标签: