Heim > Web-Frontend > js-Tutorial > JavaScript从数组的indexOf()深入之Object的Property机制_javascript技巧

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

WBOY
Freigeben: 2016-05-19 10:42:42
Original
1047 Leute haben es durchsucht

在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()); 
Nach dem Login kopieren

当然这个方法是很垃圾的。在这里具体的实现 我就不献丑了,提供一个百度上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 < )
&#63; Math.ceil(from)
: Math.floor(from);
if (from < )
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -;
};
} 
Nach dem Login kopieren

2、继承——call和apply方法

如果我们每有一个对象,那每个对象就要自己写一遍实现是不是很麻烦?

在高级语言中,我们可以用继承来解决问题,比如下面的java代码:

public class MyList<E> extends ArrayList<E>
{
public void myAdd(E e){
super.add(e);
System.out.println("Add:"+e);
}
} 
Nach dem Login kopieren

但是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(,,); 
Nach dem Login kopieren

这里可以看到:虽然用高级语言的继承方式实现了myAdd方法,但是现在myAdd方法只能传一个参数,如果要传多个参数,则需要再写一个public void myAdd(E[] e)方法,甚至是public void myAdd(List 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); 
Nach dem Login kopieren

参考是:

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)
Nach dem Login kopieren

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;
}
}); 
Nach dem Login kopieren

注释的地方不能是length,否则会无限递归。

也可以去掉set,让length变量只读。

Object.defineProperty(myobj.prototype,'length',{
get:function(){
return this.length_; //这里不能是length。
}, 
/*set:function(value){
return this.length_=value;
}*/
});
myobj.length = 3; 
Nach dem Login kopieren

这个代码会抛出异常:Uncaught TypeError: Cannot set property length of # which has only a getter。

要让对象的属性只读,还可以用writable:false,

Object.defineProperty(myobj.prototype,'length',{
writable:false
});
Nach dem Login kopieren

writable:false不能与get set共存,否则会抛出Type Error。

configurable:是否能用delete语句删除,但是configurable属性好像在严格模式下才有效,这样的代码在非严格模式下仍然能执行:(严格模式报错)

Object.defineProperty(myobj.prototype,'length',{
configurable:false
});
var obj = new myobj();
delete obj.length; 
Nach dem Login kopieren

value:指定该对象的固定值。value:10,表示这个对象初始值为10.

在非严格模式下,这样的代码不会报错,严格模式下会报错:

Object.defineProperty(myobj.prototype,'length',{
writable:false,
value:'10'
});
var obj = new myobj();
obj.length = 100; 
Nach dem Login kopieren

可以用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
Nach dem Login kopieren

这是因为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); 
Nach dem Login kopieren

可以加上一句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); 
Nach dem Login kopieren

然后运行,

又抛出了一个异常,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); 
Nach dem Login kopieren

这样,DOMTokenList也就支持了push,pop等等操作了。

Array.prototype.push.call(document.body.classList,'abc')
Nach dem Login kopieren

然后再行封装

DOMTokenList.prototype.push = function(){
Array.prototype.push.call(document.body.classList,Array.prototype.slice.call(arguments));
}
Nach dem Login kopieren

Array.prototype.slice.call(arguments)方法用于把arguments对象转换为数组。

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage