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插件 easyUI属性汇总
Jan 19 Javascript
ie浏览器使用js导出网页到excel并打印
Mar 11 Javascript
JS获取URL中参数值(QueryString)的4种方法分享
Apr 12 Javascript
浏览器窗口大小变化时使用resize事件对框架不起作用的解决方法
May 11 Javascript
node.js中Socket.IO的进阶使用技巧
Nov 04 Javascript
jQuery实现切换字体大小的方法
Mar 10 Javascript
js仿手机页面文件下拉刷新效果
Oct 14 Javascript
ES6下React组件的写法示例代码
May 04 Javascript
Angular2整合其他插件的方法
Jan 20 Javascript
解决Vue axios post请求,后台获取不到数据的问题方法
Aug 11 Javascript
vue解决使用webpack打包后keep-alive不生效的方法
Sep 01 Javascript
javascript 使用sleep函数的常见方法详解
Apr 26 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设计模式 php实现工厂模式(factory)
2015/12/07 PHP
PHP框架性能测试报告
2016/05/08 PHP
Yii2框架RESTful API 格式化响应,授权认证和速率限制三部分详解
2016/11/10 PHP
Laravel 集成微信用户登录和绑定的实现
2019/12/27 PHP
基于ThinkPHP删除目录及目录文件函数
2020/10/28 PHP
理清apply(),call()的区别和关系
2011/08/14 Javascript
js简单实现删除记录时的提示效果
2013/12/05 Javascript
jQuery实现Twitter的自动文字补齐特效
2014/11/28 Javascript
JavaScript基本数据类型及值类型和引用类型
2015/08/25 Javascript
jQuery 移动端拖拽(模块化开发,触摸事件,webpack)
2016/10/28 Javascript
Node.js中.pfx后缀文件的处理方法
2017/03/10 Javascript
JS+HTML5实现上传图片预览效果完整实例【测试可用】
2017/04/20 Javascript
vue.js实现只弹一次弹框
2018/01/29 Javascript
深入剖析Node.js cluster模块
2018/05/23 Javascript
layui table 参数设置方法
2018/08/14 Javascript
jQuery中each遍历的三种方法实例分析
2018/09/07 jQuery
vue--vuex详解
2019/04/15 Javascript
Vue 处理表单input单行文本框的实例代码
2019/05/09 Javascript
关于layui 下拉列表的change事件详解
2019/09/20 Javascript
[40:50]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS LGD第四场
2014/05/24 DOTA
[01:04:14]VP vs TNC 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
用python写扫雷游戏实例代码分享
2018/05/27 Python
对python制作自己的数据集实例讲解
2018/12/12 Python
python图像和办公文档处理总结
2019/05/28 Python
python实现批量处理将图片粘贴到另一张图片上并保存
2019/12/12 Python
Pytorch实现LSTM和GRU示例
2020/01/14 Python
浅谈pytorch、cuda、python的版本对齐问题
2020/01/15 Python
python logging 重复写日志问题解决办法详解
2020/08/04 Python
美国购物网站:Clickhere2shop
2021/01/28 全球购物
2014爱耳日宣传教育活动总结
2014/03/09 职场文书
防灾减灾标语
2014/10/07 职场文书
比赛主持人开场白
2015/05/29 职场文书
入党培养人考察意见
2015/06/08 职场文书
烛光里的微笑观后感
2015/06/17 职场文书
2015高中教师个人工作总结
2015/07/21 职场文书
2019辞职报告范本3篇!
2019/07/23 职场文书