GVKun编程网logo

将自定义函数添加到Array.prototype(可以将自定义函数名作为参数传递给另一个函数)

14

对于想了解将自定义函数添加到Array.prototype的读者,本文将是一篇不可错过的文章,我们将详细介绍可以将自定义函数名作为参数传递给另一个函数,并且为您提供关于Array,prototype.

对于想了解将自定义函数添加到Array.prototype的读者,本文将是一篇不可错过的文章,我们将详细介绍可以将自定义函数名作为参数传递给另一个函数,并且为您提供关于Array,prototype.toString.call()、Array.prototype.concat()、Array.prototype.copyWithin()、Array.prototype.every()的有价值信息。

本文目录一览:

将自定义函数添加到Array.prototype(可以将自定义函数名作为参数传递给另一个函数)

将自定义函数添加到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原型,以免弄乱任何使用plain
for..in来遍历数组的代码。

您最好创建自己的带有doSomething函数的对象构造函数类型,而不是扩展内置Array。

Object.defineProperty

现在,存在Object.defineProperty一种扩展对象原型的通用方法,而无法枚举新属性,尽管这仍然不能证明扩展 内置
类型的合理性,因为即使如此for..in,它仍然可能与其他脚本发生冲突。考虑使用两个Javascript框架的人,它们都试图以相似的方式扩展Array并选择相同的方法名称。或者,考虑有人分叉
您的 代码,然后将原始版本和分叉版本都放在同一页面上。Array对象的自定义增强功能是否仍然有效?

这是Javascript的现实,也是为什么即使使用,也应该避免修改内置类型的原型的原因Object.defineProperty。用自己的构造函数定义自己的类型。

Array,prototype.toString.call()

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()

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()

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()

Array.prototype.every()

every () 方法测试数组的所有元素是否都通过了指定函数的测试, 返回 true 或 false
只要有一个值未通过测试返回了 false,every 方法就返回 false,否则,返回 true。
callback
用来测试每个元素的函数。
thisArg
执行 callback 时使用的 this 值。
 
every 方法为数组中的每个元素执行一次 callback 函数,直到它找到一个使 callback 返回 false(表示可转换为布尔值 false 的值)的元素。如果发现了一个这样的元素,every 方法将会立即返回 false。否则,callback 为每一个元素返回 true,every 就会返回 true。callback 只会为那些已经被赋值的索引调用。不会为那些被删除或从来没被赋值的索引调用。
如果为 every 提供一个 thisArg 参数,则该参数为调用 callback 时的 this 值。如果省略该参数,则 callback 被调用时的 this 值,在非严格模式下为全局对象,在严格模式下传入 undefined。
every 不会改变原数组。
every 遍历的元素范围在第一次调用 callback 之前就已确定了。在调用 every 之后添加到数组中的元素不会被 callback 访问到。如果数组中存在的元素被更改,则他们传入 callback 的值是 every 访问到他们那一刻的值。那些被删除的元素或从来未被赋值的元素将不会被访问到。
every 和数学中的 "所有" 类似,当所有的元素都符合条件才返回 true。另外, 空数组也是返回 true。( 空数组中所有元素都符合给定的条件,注:因为空数组没有元素)。
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
polyfill
在第 5 版时,every 被添加进 ECMA-262 标准;因此在某些实现环境中不被支持。你可以把下面的代码放到脚本的开头来解决此问题,该代码允许在那些没有原生支持 every 的实现环境中使用它。该算法是 ECMA-262 第 5 版中指定的算法,假定 Object 和 TypeError 拥有它们的初始值,且 fun.call 等价于 Function.prototype.call。
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()等更多相关知识的信息可以在本站进行查询。

本文标签: