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使用手册之三 CSS操作
Mar 24 Javascript
jquery 最简单的属性菜单
Oct 08 Javascript
javascript随机抽取0-100之间不重复的10个数
Feb 25 Javascript
vue中使用refs定位dom出现undefined的解决方法
Dec 21 Javascript
Vue项目中如何引入icon图标
Mar 28 Javascript
javascript深拷贝、浅拷贝和循环引用深入理解
May 27 Javascript
基于Angular中ng-controller父子级嵌套的相关属性详解
Oct 08 Javascript
js使用formData实现批量上传
Mar 27 Javascript
Nuxt.js SSR与权限验证的实现
Nov 21 Javascript
小程序扫描普通链接二维码跳转小程序指定界面方法
May 07 Javascript
深入了解响应式React Native Echarts组件
May 29 Javascript
40行代码把Vue3的响应式集成进React做状态管理
May 20 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 Undefined index的问题
2009/06/01 PHP
destoon网站转移服务器后搜索汉字出现乱码的解决方法
2014/06/21 PHP
php读取mssql的ntext字段返回值为空的解决方法
2014/12/30 PHP
php设计模式之单例模式实例分析
2015/02/25 PHP
在 PHP 和 Laravel 中使用 Traits的方法
2019/11/13 PHP
js两行代码按指定格式输出日期时间
2011/10/21 Javascript
jCallout 轻松实现气泡提示功能
2013/09/22 Javascript
js验证输入是否为手机号码或电话号码示例
2013/12/30 Javascript
Javascript快速排序算法详解
2014/12/03 Javascript
JavaScript设计模式之工厂模式和构造器模式
2015/02/11 Javascript
全面解析JavaScript的Backbone.js框架中的Router路由
2016/05/05 Javascript
JavaScript对象数组排序实例方法浅析
2016/06/15 Javascript
js select实现省市区联动选择
2020/04/17 Javascript
getElementById().innerHTML与getElementById().value的区别
2016/10/27 Javascript
JS中绑定事件顺序(事件冒泡与事件捕获区别)
2017/01/24 Javascript
AngularJS自定义指令详解(有分页插件代码)
2017/06/12 Javascript
Angular angular-file-upload文件上传的示例代码
2018/08/23 Javascript
jQuery实现为动态添加的元素绑定事件实例分析
2018/09/07 jQuery
微信小程序 简易计算器实现代码实例
2019/09/02 Javascript
原生JS运动实现轮播图
2021/01/02 Javascript
Python httplib模块使用实例
2015/04/11 Python
PIL图像处理模块paste方法简单使用详解
2019/07/17 Python
keras中模型训练class_weight,sample_weight区别说明
2020/05/23 Python
Django Form常用功能及代码示例
2020/10/13 Python
详解html2canvas截图不能截取圆角图片的解决方案
2018/01/30 HTML / CSS
办公室前台的岗位职责
2013/12/20 职场文书
公益活动邀请函
2014/02/05 职场文书
创业大赛策划书
2014/03/01 职场文书
大学生2014全国两会学习心得体会
2014/03/10 职场文书
教研处工作方案
2014/05/26 职场文书
《中国梦我的梦》中学生演讲稿
2014/08/20 职场文书
小学生安全教育广播稿
2014/10/20 职场文书
重阳节慰问信
2015/02/15 职场文书
学校隐患排查制度
2015/08/05 职场文书
《女娲补天》教学反思
2016/02/20 职场文书
二维码条形码生成的JavaScript脚本库
2022/07/07 Javascript