JavaScript从数组的indexOf()深入之Object的Property机制


Posted in Javascript onMay 11, 2016

在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<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:,
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属性可以变啊:

JavaScript从数组的indexOf()深入之Object的Property机制

看见没,这个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对象转换为数组。

Javascript 相关文章推荐
jquery获取tr中控件值并操作tr实现思路
Mar 27 Javascript
js 获取、清空input type=&quot;file&quot;的值示例代码
Feb 19 Javascript
使用jQuery jqPlot插件绘制柱状图
Dec 18 Javascript
jQuery使用$.ajax提交表单完整实例
Dec 11 Javascript
基于jquery实现轮播焦点图插件
Mar 31 Javascript
浅谈JavaScript的内置对象和浏览器对象
Jun 03 Javascript
静态页面实现 include 引入公用代码的示例
Sep 25 Javascript
搭建element-ui的Vue前端工程操作实例
Feb 23 Javascript
vue两组件间值传递 $router.push实现方法
May 15 Javascript
jquery.pager.js分页实现详解
Jul 29 jQuery
Vue中keep-alive组件作用详解
Feb 04 Javascript
es6中class类静态方法,静态属性,实例属性,实例方法的理解与应用分析
Feb 15 Javascript
原生javascript实现分享到朋友圈功能 支持ios和android
May 11 #Javascript
使用JavaScript实现ajax的实例代码
May 11 #Javascript
jQuery的框架介绍
May 11 #Javascript
jQuery链式调用与show知识浅析
May 11 #Javascript
[原创]Javascript 实现广告后加载 可加载百度谷歌联盟广告
May 11 #Javascript
Extjs4.0 ComboBox如何实现三级联动
May 11 #Javascript
javascript简单判断输入内容是否合法的方法
May 11 #Javascript
You might like
浅谈php中include文件变量作用域
2015/06/18 PHP
Zend Framework框架之Zend_Mail实现发送Email邮件验证功能及解决标题乱码的方法
2016/03/21 PHP
记录一次排查PHP脚本执行卡住的问题
2016/12/27 PHP
基于Laravel 5.2 regex验证的正确写法
2019/09/29 PHP
PHP PDO和消息队列的个人理解与应用实例分析
2019/11/25 PHP
基于jQuery图片平滑连续滚动插件
2009/04/27 Javascript
JavaScript字符串String和Array操作的有趣方法
2012/12/18 Javascript
DOM节点删除函数removeChild()用法实例
2015/01/12 Javascript
纯js实现手风琴效果代码
2020/04/17 Javascript
Angular路由简单学习
2016/12/26 Javascript
jQuery图片轮播功能实例代码
2017/01/29 Javascript
node打造微信个人号机器人的方法示例
2018/04/26 Javascript
javascript、php关键字搜索函数的使用方法
2018/05/29 Javascript
微信小程序模拟cookie的实现
2018/06/20 Javascript
详解webpack4之splitchunksPlugin代码包分拆
2018/12/04 Javascript
js时间转换毫秒的实例代码
2019/08/21 Javascript
解决Vue的文本编辑器 vue-quill-editor 小图标样式排布错乱问题
2020/08/03 Javascript
[01:20]DOTA2 齐天大圣至宝动态展示
2016/12/13 DOTA
Python实现LRU算法的2种方法
2015/06/24 Python
Python执行时间的计算方法小结
2017/03/17 Python
使用python实现ANN
2017/12/20 Python
Python 记录日志的灵活性和可配置性介绍
2018/02/27 Python
Python 线程池用法简单示例
2019/10/02 Python
Python 内置变量和函数的查看及说明介绍
2019/12/25 Python
pytorch 自定义参数不更新方式
2020/01/06 Python
Tensorflow之MNIST CNN实现并保存、加载模型
2020/06/17 Python
python使用ctypes库调用DLL动态链接库
2020/10/22 Python
Python爬虫逆向分析某云音乐加密参数的实例分析
2020/12/04 Python
用css3制作纸张效果(外翻卷角)
2013/02/01 HTML / CSS
印度最大的网上花店:Ferns N Petals(鲜花、礼品和蛋糕)
2017/10/16 全球购物
如何写一份好的英文求职信
2014/03/19 职场文书
省级青年文明号申报材料
2014/05/23 职场文书
应届生找工作求职信
2014/06/24 职场文书
启迪人心的励志语录:脾气永远不要大于本事
2020/01/02 职场文书
MySQL中一条update语句是如何执行的
2022/03/16 MySQL
如何使用注解方式实现 Redis 分布式锁
2022/07/23 Redis