In JavaScript, arrays can be created using the Array constructor, or quickly created using [], which is also the preferred method. The array is a prototype inherited from the Object, and it does not have a special return value for typeof, it only returns 'object'.
In js, it can be said that everything is an object, and an array is also an array.
Many objects have many convenient methods such as push, concat, slice, etc. of arrays, but if some objects do not implement these methods, we still want to use these functions. So what should I do?
1. Many methods provide very efficient implementations, and we can simulate their implementations.
For example, browsers below IE8 do not support the Array indexOf method. In order to make the array support indexOf, we can write a method ourselves to implement the indexOf method:
(Press F12 to debug with IE browser to select the browser version to IE5.)
var arr = [, , , ];if (Array.prototype.indexOf) {alert("Your browser supports indexOf method.");} else {alert("Your browser does not support indexOf method.");} 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());Of course this method is very rubbish. I won’t be ugly in the specific implementation here, and provide a copy version on Baidu:
If you are interested, you can see how the v8 engine is implemented: 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. Inheritance-call and apply methods
If we have an object, isn't it very troublesome to write each object to implement it yourself?
In high-level languages, we can use inheritance to solve the problem, such as the following java code:
public class MyList<E> extends ArrayList<E>{public void myAdd(E e){super.add(e);System.out.println("Add:"+e);}}But there is no concept of inheritance in js. We can use call and apply to solve such problems.
The above code can be rewritten as:
var myObject = function(){}myObject.prototype.add = function(){Array.prototype.push.call(this,arguments);//Output argumentsfor(var i=;i<arguments.length;i++){console.log("Add:"+arguments[i]);}}var obj = new myObject();obj.add(,,);Here we can see: Although the myAdd method is implemented in the inheritance method of high-level languages, the myAdd method can only pass one parameter now. If you want to pass multiple parameters, you need to write another public void myAdd(E[] e) method, or even public void myAdd(List<E> e) method. JS can be done with one method, using arguments object to represent all the parameters input, which is difficult for high-level languages to do.
(ps, in fact, you can write public void myAdd(E...e) in java. This is an uncertain parameter. The usage of public void myAdd(E[] e) is the same)
Call and apply methods are used to change the pointer of this pointer in the function. Call has only two parameters, and apply is usually used after knowing the number of parameters. The following is an example:
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);The reference is:
apply(object,arg1,arg2,...)
call(object,[arg1,arg2,......])
Only one "array" can be followed by the call, including all parameters. apply is a syntactic sugar. If you know the number of parameters, it will be very convenient to use apply.
The above object can also be null or undefined, so this object is global object (window). For example, the above example will be followed:
var name = 'goo';alert(obj1.getName.call(null)); (In strict mode, since the global object is null, an exception will be thrown: Uncaught TypeError: Cannot read property 'name' of null)
3. Object.defineProperty
(Note: Do not use this type of feature below IE8)
Microsoft: Add properties to objects, or modify the properties of existing properties.
getters, setters,
In fact, there are also getters and setter functions for the properties of objects in js, but I personally think getters and setters in js are more like C#.
For example, the following code defines a getter/setter:
function myobj(){}Object.defineProperty(myobj.prototype,'length',{get:function(){return this.length_; //This cannot be length.},set:function(value){return this.length_=value;}});The commented place cannot be length, otherwise it will be infinitely recursive.
You can also remove set and make the length variable read-only.
Object.defineProperty(myobj.prototype,'length',{get:function(){return this.length_; //This cannot be length.}, /*set:function(value){return this.length_=value;}*/});myobj.length = 3;This code will throw an exception: Uncaught TypeError: Cannot set property length of #<myobj> which has only a getter.
To make the object's properties read-only, you can also use writable:false.
Object.defineProperty(myobj.prototype,'length',{writable:false});writable:false cannot coexist with get set, otherwise a Type Error will be thrown.
configurable: Can it be deleted with delete statement, but the configurable property seems to be valid in strict mode. Such code can still be executed in non-strict mode: (reported an error in strict mode)
Object.defineProperty(myobj.prototype,'length',{configurable:false});var obj = new myobj();delete obj.length;value: Specifies the fixed value of the object. value:10, indicating that the initial value of this object is 10.
In non-strict mode, such code will not report an error, and in strict mode, it will report an error:
Object.defineProperty(myobj.prototype,'length',{writable:false,value:'10'});var obj = new myobj();obj.length = 100;You can use getOwnPropertyDescriptor to get and modify these values. For example, now my length property is read-only.
Running such a code, but an error occurred:
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: lengthThis is because the default value of configurable is false. After calling defineProperty, configurable has the false property, so it cannot be reversed. It cannot be changed in the future.
Therefore, you must use configurable:true, and this object property can be modified. The complete code is as follows:
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);You can add a sentence descriptor.configurable = false;
It means that I have modified this property, and you can't modify it in the future
This feature is also useful in many cases. If you use call and apply methods, the length of the object is required to be variable. If the length attribute of the object is read-only, an exception will be thrown when calling call and apply.
For example, the length of the DOMTokenList object cannot be changed. I got a DOM object DOMTokenList,
But its configurable is true, we can modify it so that its length property can be changed:
See, this configurable is true, and the setter is undefined. Let's write a set method for it, isn't it OK?
var descriptor = Object.getOwnPropertyDescriptor(DOMTokenList.prototype,'length');descriptor.set = function(value){this.length = value;}Object.defineProperty(DOMTokenList.prototype,'length',descriptor);Then run,
Another exception was thrown, Uncaught RangeError: Maximum call stack size exceeded(…)
This is because when we set this.length, it will recurse infinitely in that set method we write.
Therefore, we need to use delete to eliminate the influence of the length attribute, that is:
var descriptor = Object.getOwnPropertyDescriptor(DOMTokenList.prototype,'length');descriptor.set = function(value){delete DOMTokenList.prototype.length; this.length = value;}Object.defineProperty(DOMTokenList.prototype,'length',descriptor);In this way, DOMTokenList also supports push, pop and other operations.
Array.prototype.push.call(document.body.classList,'abc')
Then encapsulate
DOMTokenList.prototype.push = function(){Array.prototype.push.call(document.body.classList,Array.prototype.slice.call(arguments));}The Array.prototype.slice.call(arguments) method is used to convert arguments objects into arrays.