在js中,可以说万物皆对象(object),一个数组也是一个对象(array)。

很多对象都有很多很方便的方法 比如数组的push,concat,slice等等,但是如果一些对象,它没有实现这些方法,我们还是想使用这些功能。那该怎么办呢?

1、很多方法都提供了非常高效的实现, 我们可以仿照它们的实现。

比如IE8以下的浏览器不支持Array的indexOf方法,为了让数组支持indexOf,我们可以自己写一个方法,实现indexOf方法:

(用IE浏览器调试 按F12,可以选择浏览器版本到IE5。)

             var arr = [1, 2, 3, 5];
if (Array.prototype.indexOf) {
alert("你的浏览器支持indexOf方法。");
} else {
alert("你的浏览器不支持indexOf方法。");
}
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(item) {
for (var i = 0; i < this.length; i++) {
if(this[i]==item){
return i;
}
}
return -1;
}
}
alert(arr.indexOf(2));
alert(arr.indexOf(8));

当然这个方法是很垃圾的。在这里具体的实现 我就不献丑了,提供一个百度上copy的版本:

有兴趣的话可以看看v8引擎是怎么实现的:https://github.com/v8/v8/blob/master/src/js/array.js

 if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt /*, from*/)
{
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}

2、继承——call和apply方法

如果我们每有一个对象,那每个对象就要自己写一遍实现是不是很麻烦?

在高级语言中,我们可以用继承来解决问题,比如下面的java代码:

 public class MyList<E> extends ArrayList<E>
{ public void myAdd(E e){
super.add(e);
System.out.println("Add:"+e); } }

但是js中没有继承的概念啊,我们可以用call和apply来解决这样的问题。

上面的代码就可以改写为:

             var myObject = function(){

             }
myObject.prototype.add = function(){
Array.prototype.push.call(this,arguments);
//输出arguments
for(var i=0;i<arguments.length;i++){
console.log("Add:"+arguments[i]);
} }
var obj = new myObject();
obj.add(1,2,3);

这里可以看到:虽然用高级语言的继承方式实现了myAdd方法,但是现在myAdd方法只能传一个参数,如果要传多个参数,则需要再写一个public void myAdd(E[] e)方法,甚至是public void myAdd(List<E> e)方法。而JS用一个方法就可以搞定,用arguments对象表示输入的所有参数,这是高级语言难以做到的。

(ps,其实在java中可以写public void myAdd(E... e),这个是不定参数,用法上public void myAdd(E[] e)是一样的)

call和apply方法用于改变函数内this指针的指向,call只有两个参数,而apply通常是知道参数个数之后才使用的,下面以例子说明:

            var Obj = function(name){
this.name = name;
} Obj.prototype.getName = function(){
return this.name;
}
var obj1 =new Obj("zou");
var obj2 = {name:'andy'};
var name = obj1.getName.call(obj2);
alert(name);

参考是:

apply(object,arg1,arg2,....)

call(object,[arg1,arg2,....])

call后面只能跟一个“数组”,包括了所有的参数。而apply则是一颗语法糖,如果知道参数的个数,用apply将很方便。

上面的object也可以是null或者undefined,这样,这个object就是global object(window),例如,还是接着上例:

var name = 'goo';
alert(obj1.getName.call(null));

(在严格模式下,由于全局对象是null,故会抛出异常:Uncaught TypeError: Cannot read property 'name' of null)

3、Object.defineProperty

(注意:不要在IE8以下使用该类特性)

微软:将属性添加到对象,或修改现有属性的特性。

getter、setter,

其实js中对于对象的属性也有getter和setter函数,不过个人觉得js中的getter和setter更像C#一些。

例如下面的代码就定义了一个getter/setter:

            function myobj(){

            }
Object.defineProperty(myobj.prototype,'length',{
get:function(){
return this.length_; //这里不能是length。
},
set:function(value){
return this.length_=value;
} });

注释的地方不能是length,否则会无限递归。

也可以去掉set,让length变量只读。

            Object.defineProperty(myobj.prototype,'length',{
get:function(){
return this.length_; //这里不能是length。
},
/*set:function(value){
return this.length_=value;
}*/ });
myobj.length = 3;

这个代码会抛出异常:Uncaught TypeError: Cannot set property length of #<myobj> which has only a getter。

要让对象的属性只读,还可以用writable:false,

            Object.defineProperty(myobj.prototype,'length',{

                writable:false

            });

writable:false不能与get set共存,否则会抛出Type Error。

configurable:是否能用delete语句删除,但是configurable属性好像在严格模式下才有效,这样的代码在非严格模式下仍然能执行:(严格模式报错)

            Object.defineProperty(myobj.prototype,'length',{

                configurable:false

            });
var obj = new myobj();
delete obj.length;

value:指定该对象的固定值。value:10,表示这个对象初始值为10.

在非严格模式下,这样的代码不会报错,严格模式下会报错:

            Object.defineProperty(myobj.prototype,'length',{

                writable:false,
value:'10' }); var obj = new myobj(); obj.length = 100;

可以用getOwnPropertyDescriptor来获取并修改这些值,比如说,现在我的length属性是只读的。

运行这样的代码,结果却报错了:

             Object.defineProperty(myobj.prototype,'length',{

                 value:10,
writable:false, });
var descriptor = Object.getOwnPropertyDescriptor(myobj.prototype,
"length"); descriptor.writable = true;
Object.defineProperty(myobj.prototype,'length',descriptor);

Uncaught TypeError: Cannot redefine property: length

这是因为configurable的默认值是false,在调用了defineProperty之后,configurable就具有false属性,这样就不能逆转了。以后就不能改了。

所以必须使用 configurable:true,这个对象属性才是可以修改的,完整的代码如下:

             Object.defineProperty(myobj.prototype,'length',{

                 value:10,
writable:false,
configurable:true });
var descriptor = Object.getOwnPropertyDescriptor(myobj.prototype,
"length"); descriptor.writable = true;
Object.defineProperty(myobj.prototype,'length',descriptor);
myobj.prototype.length = 100;
var obj = new myobj();
alert(obj.length);

可以加上一句descriptor.configurable = false;

表示这个属性我修改了,以后你们都不能再修改了

这个特性在很多时候也有用,数组Array的push pop等方法,如果使用call、apply,要求对象的length可变。如果对象的length属性只读,那么调用call、apply时,会抛出异常。

就比如DOMTokenList对象,它的length就是不可以变的。我拿到了一个DOM对象DOMTokenList,

但是它的configurable是true,我们可以修改让它的length属性可以变啊:

看见没,这个configurable是true,而setter是undefined,我们给它写一个set方法,不就可以了吗?

            var descriptor = Object.getOwnPropertyDescriptor(DOMTokenList.prototype,'length');
descriptor.set = function(value){
this.length = value;
}
Object.defineProperty(DOMTokenList.prototype,'length',descriptor);

然后运行,

又抛出了一个异常,Uncaught RangeError: Maximum call stack size exceeded(…)

这是因为,我们在set this.length时,它会在我们写的那个set方法中无限递归。

因此,我们需要使用delete消除length属性的影响,也就是:

            var descriptor = Object.getOwnPropertyDescriptor(DOMTokenList.prototype,'length');
descriptor.set = function(value){
delete DOMTokenList.prototype.length;
this.length = value;
}
Object.defineProperty(DOMTokenList.prototype,'length',descriptor);

这样,DOMTokenList也就支持了push,pop等等操作了。

Array.prototype.push.call(document.body.classList,'abc')

然后再行封装

            DOMTokenList.prototype.push = function(){
Array.prototype.push.call(document.body.classList,Array.prototype.slice.call(arguments));
}

Array.prototype.slice.call(arguments)方法用于把arguments对象转换为数组。

参考文档:

https://msdn.microsoft.com/zh-cn/library/dd548687

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

http://www.tuicool.com/articles/ju26riE

JavaScript—从数组的indexOf方法深入——Object的Property机制。的更多相关文章

  1. js 判断数组包含某值的方法 和 javascript数组扩展indexOf()方法

    var  questionId = []; var anSwerIdValue = []; ////javascript数组扩展indexOf()方法 Array.prototype.indexOf ...

  2. JavaScript——数组的indexOf()方法在IE8中的兼容性问题

    昨天在工作中遇到一个问题:数组的indexOf()方法在IE8中无效. 如以下代码在IE8中报错“对象不支持“indexOf”属性或方法”: var arr = [1,2,3]; var index ...

  3. 终于解决了IE8不支持数组的indexOf方法,array的IndexOf方法

    /* 终于解决了IE8不支持数组的indexOf方法 */ if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (el ...

  4. IE8不支持数组的indexOf方法

    在IE8下有个js错误,但是在其它浏览器下(Firefox, Chrome, IE9)下面都很正常.后来调试发现原因是在IE8下,js数组没有indexOf方法. 在使用indexOf方法前,执行一下 ...

  5. IE8不支持数组的indexOf方法 如何解决

    转自:http://www.jbxue.com/article/8367.html 原因分析: 这是一个js bug, 在IE8下,js数组没有indexOf方法,会报错:而在其它浏览器下(Firef ...

  6. 数组的indexOf方法--数组去重

    数组的indexOf方法 数组方法大家再熟悉不过了,却忽略了数组有 indexOf 这个方法(我个人感觉). 干说不练瞎扯淡,遇到了什么问题,注意⚠️点又在哪里? let arr = ['orange ...

  7. Javascript中的数组去重-indexof方法

    在Javascript中,有时我们会用到数组去重.我在这里给大家介绍一下本人认为最简单实用的一种方法-indexOf()去重. var arr = [1,1,1,2,2,2,3,3,4,5,6,2,1 ...

  8. JavaScript中数组中的方法:push()、pop()、shift()、unshift()、slice()、splice()、reverse()、join()、split()、concat()、indexOf()、forEach()、map()、

      1.创建数组的几种方法 //a).通过new来创建数组,new可以省略 var arr=new Array(); var arr=Array(); //b). .通过new来创建数组,并且赋值 v ...

  9. javascript中数组常用的方法

    在JavaScript中,数组可以使用Array构造函数来创建,或使用[]快速创建,这也是首选的方法.数组是继承自Object的原型,并且他对typeof没有特殊的返回值,他只返回'object'. ...

随机推荐

  1. Ambari服务依赖关系图生成脚本

    1. 生成服务依赖关系 #!/usr/bin/python import sys import commands import json def genDependString(ip): url=&q ...

  2. CSharpGL(39)GLSL光照示例:鼠标拖动太阳(光源)观察平行光的漫反射和镜面反射效果

    CSharpGL(39)GLSL光照示例:鼠标拖动太阳(光源)观察平行光的漫反射和镜面反射效果 开始 一图抵千言.首先来看鼠标拖动太阳(光源)的情形. 然后是鼠标拖拽旋转模型的情形. 然后我们移动摄像 ...

  3. [MongoDB] 32Bit构建上文件大小限制问题

    一. 问题概述 今天看看爬虫抓取的数据,发现数据无法插入,首先想到的就是32Bit构建的文件大小限制问题,检查一下还真的是.本文把整个检查问题,解决问题的过程记录下来. 问题:     "s ...

  4. 【PRINCE2是什么】PRINCE2认证之七大原则(7)

    我们先来回顾一下,PRINCE2七大原则分别是持续的业务验证,经验学习,角色与责任,按阶段管理,例外管理,关注产品,剪裁 第七个原则:根据项目环境剪裁 PRINCE2的价值在于它是一个通用的项目管理方 ...

  5. Swift -- 对AFN框架的封装

    Swift -- 对AFN框架的封装 一.封装AFN的目的 简单的说: 解耦 日常工作中,我们一般都不会去直接使用AFNetWorking来直接发送网络请求,因为耦合性太强,假设有多个控制器都使用AF ...

  6. 解析大型.NET ERP系统 分布式应用模式设计与实现

    C/S架构的应用程序,将一些复杂的计算逻辑由客户端转移到服务器端可以改善性能,同时也为了其它方面的控制..NET Remoting在局域网内调用的性能相当不错.ERP系统中基于.NET Remotin ...

  7. Android 开发环境在 Windows7 下的部署安装

    Android SDK Android SDK 为 Android 应用的开发.测试和调试提了必要的API库和开发工具. ADT Bundle 下载 如果你是一个android 开发新手,推荐你下载使 ...

  8. JavaScript权威设计--jQuery,Ajax.animate,SVG(简要学习笔记二十)[完结篇]

    1.$和jquery在全局命名空间中定义的唯一两个变量.   2.jquery是工厂函数,不是构造函数.他返回一个新创建的对象.   3.jquery的四种调用方式:     <1>传递C ...

  9. Node.js:events事件模块

    Nodejs的大部分核心API都是基于异步事件驱动设计的,所有可以分发事件的对象都是EventEmitter类的实例. 大家知道,由于nodejs是单线程运行的,所以nodejs需要借助事件轮询,不断 ...

  10. SQLServer:什么是主键(PK)和外键(FK)?

    一.主键与外键 1.主键是用来唯一地标识一行数据.主键列必须包含唯一的值,且不能包含空值(null). 2.主键可以建立在每张二维表中单列或者多列上. 3.一张二维表上的外键可以引用另一张二维表上对应 ...