JavaScript从数组的indexOf()深入之Object的Property机制_javascript技巧
在JavaScript中,数组可以使用Array构造函数来创建,或使用[]快速创建,这也是首选的方法。数组是继承自Object的原型,并且他对typeof没有特殊的返回值,他只返回'object'。
js中,可以说万物皆对象(object),一个数组也是一个对象(array)。
很多对象都有很多很方便的方法 比如数组的push,concat,slice等等,但是如果一些对象,它没有实现这些方法,我们还是想使用这些功能。那该怎么办呢?
1、很多方法都提供了非常高效的实现, 我们可以仿照它们的实现。
比如IE8以下的浏览器不支持Array的indexOf方法,为了让数组支持indexOf,我们可以自己写一个方法,实现indexOf方法:
(用IE浏览器调试 按F12,可以选择浏览器版本到IE5。)
var arr = [, , , ]; if (Array.prototype.indexOf) { alert("你的浏览器支持indexOf方法。"); } else { alert("你的浏览器不支持indexOf方法。"); } if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(item) { for (var i = ; i < this.length; i++) { if(this[i]==item){ return i; } } return -; } } alert(arr.indexOf()); alert(arr.indexOf());
当然这个方法是很垃圾的。在这里具体的实现 我就不献丑了,提供一个百度上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 >>> ; var from = Number(arguments[]) || ; from = (from < ) ? Math.ceil(from) : Math.floor(from); if (from < ) from += len; for (; from < len; from++) { if (from in this && this[from] === elt) return from; } return -; }; }
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=;i<arguments.length;i++){ console.log("Add:"+arguments[i]); } } var obj = new myObject(); obj.add(,,);
这里可以看到:虽然用高级语言的继承方式实现了myAdd方法,但是现在myAdd方法只能传一个参数,如果要传多个参数,则需要再写一个public void myAdd(E[] e)方法,甚至是public void myAdd(List
(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 #
要让对象的属性只读,还可以用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:, 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:, writable:false, configurable:true }); var descriptor = Object.getOwnPropertyDescriptor(myobj.prototype, "length"); descriptor.writable = true; Object.defineProperty(myobj.prototype,'length',descriptor); myobj.prototype.length = ; 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对象转换为数组。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

JavaScript字符串替换方法详解及常见问题解答 本文将探讨两种在JavaScript中替换字符串字符的方法:在JavaScript代码内部替换和在网页HTML内部替换。 在JavaScript代码内部替换字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 该方法仅替换第一个匹配项。要替换所有匹配项,需使用正则表达式并添加全局标志g: str = str.replace(/fi

利用轻松的网页布局:8个基本插件 jQuery大大简化了网页布局。 本文重点介绍了简化该过程的八个功能强大的JQuery插件,对于手动网站创建特别有用

因此,在这里,您准备好了解所有称为Ajax的东西。但是,到底是什么? AJAX一词是指用于创建动态,交互式Web内容的一系列宽松的技术。 Ajax一词,最初由Jesse J创造

10款趣味横生的jQuery游戏插件,让您的网站更具吸引力,提升用户粘性!虽然Flash仍然是开发休闲网页游戏的最佳软件,但jQuery也能创造出令人惊喜的效果,虽然无法与纯动作Flash游戏媲美,但在某些情况下,您也能在浏览器中获得意想不到的乐趣。 jQuery井字棋游戏 游戏编程的“Hello world”,现在有了jQuery版本。 源码 jQuery疯狂填词游戏 这是一个填空游戏,由于不知道单词的上下文,可能会产生一些古怪的结果。 源码 jQuery扫雷游戏

本教程演示了创建通过Ajax加载的动态页面框,从而可以即时刷新,而无需全页重新加载。 它利用jQuery和JavaScript。将其视为自定义的Facebook式内容框加载程序。 关键概念: Ajax和JQuery

本教程演示了如何使用jQuery创建迷人的视差背景效果。 我们将构建一个带有分层图像的标题横幅,从而创造出令人惊叹的视觉深度。 更新的插件可与JQuery 1.6.4及更高版本一起使用。 下载

此JavaScript库利用窗口。名称属性可以管理会话数据,而无需依赖cookie。 它为浏览器中存储和检索会话变量提供了强大的解决方案。 库提供了三种核心方法:会话
