JavaScript—从数组的indexOf方法深入——Object的Property机制。

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

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

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

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

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

 1             var arr = [1, 2, 3, 5];
 2             if (Array.prototype.indexOf) {  3 alert("你的浏览器支持indexOf方法。");  4 } else {  5 alert("你的浏览器不支持indexOf方法。");  6  }  7 if (!Array.prototype.indexOf) {  8 Array.prototype.indexOf = function(item) {  9 for (var i = 0; i < this.length; i++) { 10 if(this[i]==item){ 11 return i; 12  } 13  } 14 return -1; 15  } 16  } 17 alert(arr.indexOf(2)); 18 alert(arr.indexOf(8));

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

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

 1 if (!Array.prototype.indexOf)
 2 {
 3 Array.prototype.indexOf = function(elt /*, from*/)  4  {  5 var len = this.length >>> 0;  6 var from = Number(arguments[1]) || 0;  7 from = (from < 0)  8 ? Math.ceil(from)  9  : Math.floor(from); 10 if (from < 0) 11 from += len; 12 for (; from < len; from++) 13  { 14 if (from in this && 15 this[from] === elt) 16 return from; 17  } 18 return -1; 19  }; 20 }

2、继承——call和apply方法

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

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

 1 public class MyList<E> extends ArrayList<E>
 2 {  3  4 public void myAdd(E e){  5 super.add(e);  6 System.out.println("Add:"+e);  7  8  }  9 10 }

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

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

 1             var myObject = function(){
 2
 3  4  }  5 myObject.prototype.add = function(){  6 Array.prototype.push.call(this,arguments);  7 //输出arguments  8 for(var i=0;i<arguments.length;i++){  9 console.log("Add:"+arguments[i]); 10  } 11 12  } 13 var obj = new myObject(); 14 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属性是只读的。

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

 1             Object.defineProperty(myobj.prototype,'length',{
 2
 3                 value:10,
 4 writable:false,  5  6  7  8  });  9 var descriptor = Object.getOwnPropertyDescriptor(myobj.prototype, 10 "length"); 11 12 descriptor.writable = true; 13 Object.defineProperty(myobj.prototype,'length',descriptor);

Uncaught TypeError: Cannot redefine property: length

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

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

 1             Object.defineProperty(myobj.prototype,'length',{
 2
 3                 value:10,
 4 writable:false,  5 configurable:true  6  7  8  });  9 var descriptor = Object.getOwnPropertyDescriptor(myobj.prototype, 10 "length"); 11 12 descriptor.writable = true; 13 Object.defineProperty(myobj.prototype,'length',descriptor); 14 myobj.prototype.length = 100; 15 var obj = new myobj(); 16 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

转载于:https://www.cnblogs.com/lyh0501/p/5479355.html

JavaScript—从数组的indexOf方法深入——Object的Property机制。相关推荐

  1. 解决IE8不支持数组的indexOf方法

    今天,测试报过来一个js bug, 在IE8下有个js错误,但是在其它浏览器下(Firefox, Chrome, IE9)下面都很正常. 后来调试发现原因是在IE8下, js数组没有indexOf方法 ...

  2. 数组的indexOf 方法

    1: 数组的indexOf 方法:String 类型的使用let str = 'orange';str.indexOf('o'); // 0 字符串中出现字母 o 的位置str.indexOf('n' ...

  3. php 数组 indexof,详解js中字符串和数组的indexof方法

    javascript:里判断字符串是否包涵某个子字符串时,我们经常会遇到indexOf这个方法.但是你可知道,indexOf不仅仅可以用在字符串里,还可以用在数组里. 首先我们来认识一下indexOf ...

  4. Javascript中数组的sort方法的分析

    定义和用法 JavaScript中sort() 方法用于对数组的元素进行排序, 返回一个元素已经进行了排序的 Array 对象 语法 arrayObject.sort(sortby) 参数 描述 so ...

  5. #JavaScript# 合并数组的多种方法 (附源码)

    方法1:concat() concat()方法合并数组,不改变原来的两个数组,数据不变,同时会返回一个新的数组 //concat()方法合并数组不改变原数组,用一个新的数组来接收2个或多个数组的数据g ...

  6. Javascript数组常见的方法

    分为两种:原型(实例)方法和静态函数 第一类:原型方法: 栈方法(后进先出) push 参数:任意个 作用:将任意数量的参数逐个添加到数组尾部 返回值: 新数组的长度 影响:破坏了原数组 pop 参数 ...

  7. vue 中indexof_JS数组中的indexOf方法

    前言 这两天在家中帮朋友做项目,项目中使用了数组的indexOf 方法,找到了一篇文章,感觉非常不错,顺便整理下以防链接丢失. 相信说到 indexOf 大家并不陌生,判断字符串是否包涵子字符串时特别 ...

  8. Python JavaScript 的数组去重

    "数组去重" 是实际应用中常用的操作,出现在面试题中的概率也很高,今天简述一下 Python 和 JavaScript 中数组去重的方法 python数组去重 >>&g ...

  9. js indexOf使用及注意事项以及数组使用indexOf

    一.定义和用法 indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置. 语法 stringObject.indexOf(searchvalue,fromindex) 参数 描述 s ...

  10. 面试题——4种数组去重的方法

    数组去重或者其衍生作为笔试题或者机试题出现的几率也是很大的,写出的方法越多,则让面试官觉得你思维越开阔,那么成功的几率当然就大了. 废话不多说,下面来说说下面我整理的4中数组去重的方法 方法一: fi ...

最新文章

  1. 7个值得推荐的优质软件,让人忍不住体验!
  2. 【数据库】Redis/MongoDB/MySQL/Oracle随笔索引
  3. 有三AI模拟面试服务上线,一对一服务助你求职
  4. 深度神经网络的反向传播算法数学推导
  5. java温度计的实现_echart 之实现温度计
  6. Microwindows及基于Nano-X的简单程序开发
  7. 16.U-boot的工作流程分析-2440
  8. [PHP] 面向对象
  9. 一个好的桌面图标的网站
  10. 关于M1芯片版Mac安装Photoshop后储存psd时出现程序错误提示的解决方法
  11. 《Advanced .NET Debugging》 读书笔记 Listing 5-6: Pining的简单示例
  12. Web全栈工程师养成记
  13. 中国区块链专利申请数破万:阿里巴巴、联通、复杂美稳居前三甲
  14. 如何用计算机算工资,个人工资计算器避税
  15. 织梦网站模板的建站优势
  16. 漫画程序猿惯用口头禅
  17. Android 问题:Installation failed due to: ‘Invalid File:
  18. QQ强制加好友链接,对方不同意我也加
  19. android 权限模型,android-棉花糖权限模型在Airbnb中的AccountManager getAccounts()异常,该怎么做?...
  20. 把当前web页面上的所有图片复制到特定目录

热门文章

  1. 【机器学习系列】MCMC第四讲:Metropolis Hastings采样算法
  2. 2017年度工作总结
  3. 数据库基础(2)选择,投影,连接,除法运算
  4. 无限宽神经网络 - 神经网络正切核理论【NTK】
  5. 使用Limelighter生成伪造代码签名
  6. Raki的读paper小记:TriggerNER: Learning with Entity Triggers as Explanations for Named Entity Recognition
  7. Hugging Face Course-Introduction学习小记 (part1)
  8. linux挂载硬盘不适合重启,Linux分区g挂载目录不重启挂载硬盘
  9. get() got an unexpected keyword argument
  10. mysql数据库的索引及优化小技巧!