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 相关文章推荐
JS 显示当前日期与时间的代码
Mar 24 Javascript
如何制作浮动广告 JavaScript制作浮动广告代码
Dec 30 Javascript
在Iframe中获取父窗口中表单的值(示例代码)
Nov 22 Javascript
jquery实现图片水平滚动效果代码分享
Aug 26 Javascript
jQuery插件HighCharts绘制的基本折线图效果示例【附demo源码下载】
Mar 07 Javascript
JS实现json的序列化和反序列化功能示例
Jun 13 Javascript
vue实现百度搜索下拉提示功能实例
Jun 14 Javascript
js学习总结_基于数据类型检测的四种方式(必看)
Jul 04 Javascript
JS实现图片轮播效果实例详解【可自动和手动】
Apr 04 Javascript
小程序封装路由文件和路由方法(5种全解析)
May 26 Javascript
在Uni中使用Vue的EventBus总线机制操作
Jul 31 Javascript
JavaScript async/await原理及实例解析
Dec 02 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
浅谈apache和nginx的rewrite的区别
2013/02/22 PHP
怎么在Windows系统中搭建php环境
2013/08/31 PHP
jquery实现页面图片等比例放大缩小功能
2014/02/12 Javascript
浅谈Javascript中深复制
2014/12/01 Javascript
node.js中的buffer.Buffer.byteLength方法使用说明
2014/12/10 Javascript
jQuery中height()方法用法实例
2014/12/24 Javascript
JavaScript的jQuery库插件的简要开发指南
2015/08/12 Javascript
基于jQuery实现仿微博发布框字数提示
2016/07/27 Javascript
完美解决UI-Grid表格元素中多个空格显示为一个空格的问题
2017/04/25 Javascript
深入理解Angular中的依赖注入
2017/06/26 Javascript
jQuery remove()过滤被删除的元素(推荐)
2017/07/18 jQuery
日期时间范围选择插件:daterangepicker使用总结(必看篇)
2017/09/14 Javascript
详解vue项目的构建,打包,发布全过程
2017/11/23 Javascript
JavaScript实现写入文件到本地的方法【基于FileSaver.js插件】
2018/03/15 Javascript
vue-router 手势滑动触发返回功能
2018/09/30 Javascript
node.js使用express框架进行文件上传详解
2019/03/03 Javascript
nodejs中request库使用HTTPS代理的方法
2019/04/30 NodeJs
使用Vue CLI创建typescript项目的方法
2019/08/09 Javascript
vue 虚拟DOM的原理
2020/10/03 Javascript
[49:08]Secret vs VP 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
关于pytorch多GPU训练实例与性能对比分析
2019/08/19 Python
Python 识别12306图片验证码物品的实现示例
2020/01/20 Python
英国领先的家庭时尚品牌:Peacocks
2018/01/11 全球购物
英国信箱在线鲜花速递公司:Bloom & Wild
2019/03/10 全球购物
C#面试问题
2016/07/29 面试题
物流专业大学的自我评价
2014/01/11 职场文书
美容院经理岗位职责
2014/04/03 职场文书
党员群众路线承诺书
2014/05/20 职场文书
三年级上册科学教学计划
2015/01/21 职场文书
酒店工程部主管岗位职责
2015/04/16 职场文书
小学中队委竞选稿
2015/11/20 职场文书
2019新员工心得体会
2019/06/25 职场文书
高一作文之暖冬
2019/11/09 职场文书
使用Pytorch实现two-head(多输出)模型的操作
2021/05/28 Python
Element-ui Layout布局(Row和Col组件)的实现
2021/12/06 Vue.js
索尼ICF-36收音机评测
2022/04/30 无线电