Article directory
Instance object and new command
what is the object
Object-oriented programming (Object Oriented Programming, abbreviated as OOP) is currently the mainstream programming paradigm. It abstracts various complex relationships in the real world into individual objects, and then completes the simulation of the real world through the division of labor and cooperation between objects.
Each object is a functional center with a clear division of labor and can complete tasks such as receiving information, processing data, and sending information. Objects can be reused and customized through the inheritance mechanism. Therefore, object-oriented programming has the characteristics of flexibility, code reusability, and high modularity, and is easy to maintain and develop. Compared with traditional procedural programming (procedural programming) consisting of a series of functions or instructions, it is more suitable for multi-person cooperation. Large software projects.
So, what exactly is an "object"? We understand it on two levels.
(1) An object is an abstraction of a single object.
A book, a car, and a person can all be objects, as can a database, a web page, and a remote server connection. When objects are abstracted into objects, the relationship between objects becomes the relationship between objects, so that the real situation can be simulated and programming for objects can be performed.
(2) An object is a container that encapsulates properties and methods.
Properties are the state of the object, and methods are the behavior of the object (accomplishing some kind of task). For example, we can abstract an animal into animal
an object, use "attributes" to record which animal it is, and use "methods" to represent certain behaviors of animals (running, hunting, resting, etc.).
Constructor
The first step in object-oriented programming is to generate objects. As mentioned earlier, an object is an abstraction of a single entity. Usually a template is needed to represent the common characteristics of a certain type of object, and then the object is generated according to this template.
Typical object-oriented programming languages (such as C++ and Java) have the concept of "class". The so-called "class" is the template of the object, and the object is the instance of the "class". However, the object system of the JavaScript language is not based on "classes", but on constructors and prototype chains.
The JavaScript language uses constructors as templates for objects. The so-called "constructor" is a function specially used to generate instance objects. It is the template of the object, describing the basic structure of the instance object. A constructor can generate multiple instance objects, all of which have the same structure.
A constructor is just an ordinary function, but with its own characteristics and usage.
var Vehicle = function () {
this.price = 1000;
};
In the above code, Vehicle
it is the constructor. In order to distinguish it from ordinary functions, the first letter of the constructor name is usually capitalized.
There are two characteristics of the constructor.
- The keyword is used inside the function body
this
, which represents the object instance to be generated. - When generating objects,
new
commands must be used.
The constructor basically does not return a value
The commands are introduced first new
.
new command
basic usage
new
The role of the command is to execute the constructor and return an instance object.
var Vehicle = function () {
this.price = 1000;
};
var v = new Vehicle();
v.price // 1000
The above code uses new
the command to let the constructor Vehicle
generate an instance object and save it in a variable v
. This newly generated instance object Vehicle
gets price
properties from the constructor. new
When the command is executed, the inside of the constructor this
represents the newly generated instance object, this.price
indicating that the instance object has an price
attribute with a value of 1000.
When using new
commands, the constructor can also accept parameters as needed.
var Vehicle = function (p) {
this.price = p;
};
var v = new Vehicle(500);
v.price // 500
new
The command itself can execute the constructor, so the following constructor can either have parentheses or not. The following two lines of code are equivalent, but to indicate that this is a function call, parentheses are recommended.
// 推荐的写法
var v = new Vehicle();
// 不推荐的写法
var v = new Vehicle;
A natural question is, new
what happens if you forget to use the command and just call the constructor?
In this case, the constructor becomes an ordinary function and does not generate an instance object. And because of the reasons that will be mentioned later, this
representing the global object at this time will cause some unexpected results.
var Vehicle = function (){
this.price = 1000;
};
var v = Vehicle();
v // undefined
price // 1000
In the above code, Vehicle
when calling the constructor, I forgot to add new
the command. As a result, since the Vehicle function has no return value, the variable v
becomes undefined
and price
the property becomes a global variable . new
Therefore, great care should be taken to avoid direct calls to constructors without a command.
In order to ensure that the constructor must new
be used with the command, a solution is to use strict mode inside the constructor, that is, the first line is added use strict
. In this case, once you forget to use new
the command, calling the constructor directly will report an error.
function Fubar(foo, bar){
'use strict';
this._foo = foo;
this._bar = bar;
}
Fubar()
// TypeError: Cannot set property '_foo' of undefined
The above code Fubar
is a constructor, and use strict
the command ensures that the function runs in strict mode. Because in strict mode, the internal this
object of the function cannot point to the global object, which is equal to by default undefined
, and an error will be reported if it is not new
called (JavaScript does not allow undefined
adding attributes).
Another solution is to judge whether to use the command inside the constructor new
, and return an instance object directly if it is found that it is not used.
function Fubar(foo, bar) {
if (!(this instanceof Fubar)) {
return new Fubar(foo, bar);
}
this._foo = foo;
this._bar = bar;
}
Fubar(1, 2)._foo // 1
(new Fubar(1, 2))._foo // 1
The constructor in the above code new
will get the same result no matter whether the command is added or not.
The principle of the new command
When a command is used new
, the function following it executes the following steps in sequence.
- Create an empty object as the object instance to be returned.
- Point the prototype of this empty object to the properties of the constructor
prototype
. - Assign this empty object to
this
the keyword inside the function. - Start executing the code inside the constructor.
In other words, inside the constructor, this
it refers to a newly generated empty object, and all targeted this
operations will happen on this empty object. The reason why a constructor is called a "constructor" means that the purpose of this function is to manipulate an empty object (that is, this
an object) and "construct" it into what it needs.
If there is a statement inside the constructor return
and it return
is followed by an object, new
the command will return return
the object specified by the statement; otherwise, it will return
return the this
object regardless of the statement.
var Vehicle = function () {
this.price = 1000;
return 1000;
};
(new Vehicle()) === 1000
// false
In the code above, the constructor Vehicle
statement return
returns a value. At this time, new
the command will ignore this return
statement and return the "constructed" this
object.
However, if return
the statement returns an unrelated this
new object, new
the command returns the new object instead of this
the object. This point needs special attention.
var Vehicle = function (){
this.price = 1000;
return {
price: 2000 };
};
(new Vehicle()).price
// 2000
Vehicle
In the above code, the statement of the constructor return
returns a new object. new
The command returns this object, not this
the object.
On the other hand, if the command this
is used on a normal function (a function without keywords inside) new
, it will return an empty object.
function getMessage() {
return 'this is a message';
}
var msg = new getMessage();
msg // {}
typeof msg // "object"
In the above code, getMessage
it is an ordinary function that returns a string. Using new
commands on it, you get an empty object. This is because new
commands always return an object, either the instance object or return
the object specified by the statement. In this case, return
the statement returns a string, so new
the command ignores the statement.
new
The simplified internal process of the command can be represented by the following code.
function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
// 将 arguments 对象转为数组
var args = [].slice.call(arguments);
// 取出构造函数
var constructor = args.shift();
// 创建一个空对象,继承构造函数的 prototype 属性
var context = Object.create(constructor.prototype);
// 执行构造函数
var result = constructor.apply(context, args);
// 如果返回结果是对象,就直接返回,否则返回 context 对象
return (typeof result === 'object' && result != null) ? result : context;
}
// 实例
var actor = _new(Person, '张三', 28);
this keyword
meaning
this
Keywords are a very important grammatical point. It is no exaggeration to say that most development tasks cannot be accomplished without understanding its implications.
As mentioned in the previous chapter, this
it can be used in constructors to represent instance objects. In addition, this
it can also be used in other occasions. But no matter what the occasion, this
there is one thing in common: it always returns an object.
Simply put, this
it is the object where the property or method "currently" resides.
this.property
In the above code, this
it represents property
the object where the attribute is currently located.
Below is a practical example.
var person = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
person.describe()
// "姓名:张三"
In the above code, this.name
it means name
the object where the attribute is located. Since this.name
it is describe
called in a method, and describe
the current object where the method is located is person
, it this
points to person
, this.name
that is person.name
.
Since the properties of an object can be assigned to another object, the current object where the property is located is mutable, that is, the this
pointer to is mutable.
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe()
// "姓名:李四"
In the above code, A.describe
the attribute is assigned to B
, so B.describe
it means that describe
the current object where the method is located is B
, so this.name
it points to B.name
.
By refactoring this example a little bit, this
the dynamic pointing of 's can be seen more clearly.
function f() {
return '姓名:'+ this.name;
}
var A = {
name: '张三',
describe: f
};
var B = {
name: '李四',
describe: f
};
A.describe() // "姓名:张三"
B.describe() // "姓名:李四"
In the above code, keywords f
are used inside the function this
, and the pointers are different depending on f
the object it is located in this
.
As long as the function is assigned to another variable, this
the pointer to will change.
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var name = '李四';
var f = A.describe;
f() // "姓名:李四"
In the above code, A.describe
if it is assigned to a variable f
, the internal one this
will point to f
the object where it is running (in this case, the top-level object).
Let's look at another example of web programming.
<input type="text" name="age" size=3 onChange="validate(this, 18, 99);">
<script>
function validate(obj, lowval, hival){
if ((obj.value < lowval) || (obj.value > hival))
console.log('Invalid Value!');
}
</script>
The above code is a text input box. Whenever the user enters a value, onChange
the callback function will be called to verify whether the value is within the specified range. The browser will pass the current object to the callback function, so this
it means passing in the current object (that is, the text box), and then this.value
the user's input value can be read from it.
To sum up, in the JavaScript language, everything is an object, and the operating environment is also an object, so functions run in an object, which this
is the object (environment) where the function runs. This will not confuse users, but JavaScript supports dynamic switching of the running environment, that is to say, this
the pointer is dynamic, and there is no way to determine in advance which object to point to. This is what confuses beginners the most.
substance
The reason why the JavaScript language has this design is related to the data structure in the memory.
var obj = {
foo: 5 };
The above code assigns an object to a variable obj
. The JavaScript engine will first generate an object in memory { foo: 5 }
, and then assign the memory address of the object to a variable obj
. In other words, a variable obj
is an address (reference). If you want to read later obj.foo
, the engine first obj
gets the memory address, then reads the original object from the address, and returns its foo
attributes.
The original object is stored in a dictionary structure, and each attribute name corresponds to an attribute description object. For example, foo
the properties of the above example are actually saved in the following form.
{
foo: {
[[value]]: 5
[[writable]]: true
[[enumerable]]: true
[[configurable]]: true
}
}
Note that foo
the value of the attribute is stored in value
the attribute of the attribute description object.
Such a structure is very clear, the problem is that the value of the attribute may be a function.
var obj = {
foo: function () {
} };
At this time, the engine will save the function in memory separately, and then assign the address of the function to the attribute foo
of value
the attribute.
{
foo: {
[[value]]: 函数的地址
...
}
}
Since a function is a single value, it can be executed in different environments (contexts).
var f = function () {
};
var obj = {
f: f };
// 单独执行
f()
// obj 环境执行
obj.f()
JavaScript allows references to other variables of the current environment inside the body of a function.
var f = function () {
console.log(x);
};
In the above code, variables are used in the function body x
. This variable is provided by the runtime environment.
Now the problem comes, since the function can be executed in different operating environments, there needs to be a mechanism to obtain the current operating environment (context) inside the function body. Therefore, this
it appeared, and its design purpose is to refer to the current operating environment of the function inside the function body.
var f = function () {
console.log(this.x);
}
In the above code, the function body this.x
refers to the current operating environment x
.
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 单独执行
f() // 1
// obj 环境执行
obj.f() // 2
In the above code, the function f
is executed in the global environment, this.x
pointing to the global environment ; executing x
in the environment, pointing to .obj
this.x
obj.x
occasions of use
this
There are mainly the following usage occasions.
(1) Global environment
Used by the global environment this
, it refers to the top-level object window
.
this === window // true
function f() {
console.log(this === window);
}
f() // true
The above code shows that whether it is inside a function or not, as long as it is running in the global environment, this
it refers to the top-level object window
.
(2) Constructor
In the constructor this
, it refers to the instance object.
var Obj = function (p) {
this.p = p;
};
The above code defines a constructor Obj
. Since this
it points to the instance object, it is defined inside the constructor this.p
, which is equivalent to defining an p
attribute of the instance object.
var o = new Obj('Hello World!');
o.p // "Hello World!"
(3) Object method
If the method of the object contains this
, this
the pointer to is the object where the method is running. Assigning this method to another object will change this
the pointer.
However, this rule is not easy to grasp. Please see the code below.
var obj ={
foo: function () {
console.log(this);
}
};
obj.foo() // obj
In the above code, obj.foo
when the method is executed, its internal this
pointer obj
.
However, the following usages will change this
the direction.
// 情况一
(obj.foo = obj.foo)() // window
// 情况二
(false || obj.foo)() // window
// 情况三
(1, obj.foo)() // window
In the above code, obj.foo
it is a value. When this value is actually called, the operating environment is no longer obj
, but the global environment, so this
it no longer points to it obj
.
It can be understood that inside the JavaScript engine, obj
and obj.foo
stored in two memory addresses, called address one and address two. obj.foo()
When calling in this way, address 2 is called from address 1, so the operating environment of address 2 is address 1, this
pointing to obj
. However, in the above three cases, the address 2 is directly taken out and called. In this case, the operating environment is the global environment, so it this
points to the global environment. The above three cases are equivalent to the code below.
// 情况一
(obj.foo = function () {
console.log(this);
})()
// 等同于
(function () {
console.log(this);
})()
// 情况二
(false || function () {
console.log(this);
})()
// 情况三
(1, function () {
console.log(this);
})()
If this
the method is not in the first layer of the object, it this
only points to the object of the current layer and does not inherit the upper layer.
var a = {
p: 'Hello',
b: {
m: function() {
console.log(this.p);
}
}
};
a.b.m() // undefined
In the above code, a.b.m
the method is in a
the second layer of the object, and the inside of the method this
is not pointing a
, but pointing a.b
, because the actual execution is the following code.
var b = {
m: function() {
console.log(this.p);
}
};
var a = {
p: 'Hello',
b: b
};
(a.b).m() // 等同于 b.m()
If you want to achieve the desired effect, you can only write it as follows.
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
If you assign the method inside the nested object to a variable at this time, it this
will still point to the global object.
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
var hello = a.b.m;
hello() // undefined
In the above code, m
it is a method inside the multi-layer object. For simplicity, it is assigned to hello
a variable, and when the result is called, this
it points to the top-level object. In order to avoid this problem, you can only m
assign the object where it is located hello
, so that when you call it, this
the pointer to it will not change.
var hello = a.b;
hello.m() // Hello
Points to note
Avoid multiple layers of this
Since this
the pointer to is indeterminate, never include multiple layers in a function this
.
var o = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this);
}();
}
}
o.f1()
// Object
// Window
The above code contains two layers this
. After running, the first layer points to the object o
, and the second layer points to the global object, because the actual execution is the following code.
var temp = function () {
console.log(this);
};
var o = {
f1: function () {
console.log(this);
var f2 = temp();
}
}
A workaround is to use a this
variable on the second level that points to the outer level instead.
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
// Object
// Object
The above code defines a variable that
, which is fixed to the outer layer this
, and then used in the inner layer , so the pointing that
will not change.this
this
In fact, it is a very common practice to use a fixed value of a variable and then call this variable in the inner function, please be sure to master it.
JavaScript provides strict mode, which can also avoid this kind of problem. In strict mode, if the object inside the function this
points to the top-level object, an error will be reported.
var counter = {
count: 0
};
counter.inc = function () {
'use strict';
this.count++
};
var f = counter.inc;
f()
// TypeError: Cannot read property 'count' of undefined
In the above code, the method adopts strict mode inc
through the declaration. At this time , once the internal object points to the top-level object, an error will be reported.'use strict'
this
Avoid this in array handling methods
The array's map
and foreach
methods allow a function to be supplied as an argument. This function should not be used internally this
.
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
});
}
}
o.f()
// undefined a1
// undefined a2
In the above code, foreach
the callback function of the method this
actually points to window
the object, so o.v
the value cannot be obtained. this
The reason is the same as the multi-layer in the previous paragraph , that is, the inner layer this
does not point to the outside, but points to the top-level object.
One way to solve this problem, as mentioned earlier, is to use intermediate variable fixation this
.
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
var that = this;
this.p.forEach(function (item) {
console.log(that.v+' '+item);
});
}
}
o.f()
// hello a1
// hello a2
Another way is to fix its runtime environment as the second parameter of the method this
.foreach
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
}, this);
}
}
o.f()
// hello a1
// hello a2
Avoid this in the callback function
Callbacks this
often change pointers and are best avoided.
var o = new Object();
o.f = function () {
console.log(this === o);
}
// jQuery 的写法
$('#button').on('click', o.f);
In the above code, after clicking the button, the console will be displayed false
. The reason is that this time this
it is no longer pointing to o
the object, but to the DOM object of the button, because f
the method is called in the context of the button object. This subtle difference is easily overlooked in programming, leading to hard-to-detect errors.
In order to solve this problem, the following methods can be used to this
bind the object, that is, to make it this
fixed to a certain object and reduce uncertainty.
The method that binds this
this
The dynamic switching of , of course, creates great flexibility for JavaScript, but it also makes programming difficult and ambiguous. Sometimes, it is necessary to this
fix it to avoid unexpected situations. JavaScript provides call
, apply
, bind
these three methods to switch/fix this
the pointing.
Function.prototype.call()
For the method of a function instance call
, you can specify the internal this
pointer of the function (that is, the scope where the function is executed), and then call the function in the specified scope.
var obj = {
};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
In the above code, f
when the function is run in the global environment, this
it points to the global environment (the browser is window
an object); call
the method can change this
the pointing, specify this
the pointing object obj
, and then obj
run the function in the scope of the object f
.
call
The method parameter, which should be an object. If the parameter is empty, null
and undefined
, the global object is passed in by default.
var n = 123;
var obj = {
n: 456 };
function a() {
console.log(this.n);
}
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456
In the above code, if the keyword a
in the function points to the global object, the return result is . If the method is used to point the keyword to an object, the return result is . It can be seen that if the method has no parameters, or the parameter is or , it is equivalent to pointing to the global object.this
123
call
this
obj
456
call
null
undefined
If call
the parameter of the method is an original value, the original value will be automatically converted into the corresponding wrapper object, and then passed into call
the method.
var f = function () {
return this;
};
f.call(5)
// Number {[[PrimitiveValue]]: 5}
In the above code, call
the parameter 5
is not an object, it will be automatically converted into a wrapper object ( Number
instance), and bound f
internally this
.
call
Methods can also accept multiple parameters.
func.call(thisValue, arg1, arg2, ...)
call
The first parameter of is this
the object to be pointed to, and the following parameters are the parameters required when the function is called.
function add(a, b) {
return a + b;
}
add.call(this, 1, 2) // 3
In the above code, call
the method specifies the binding current environment (object) add
inside the function this
, and the parameters are 1
and 2
, so add
it is obtained after the function runs 3
.
call
One application of methods is to call native methods on objects.
var obj = {
};
obj.hasOwnProperty('toString') // false
// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function () {
return true;
};
obj.hasOwnProperty('toString') // true
Object.prototype.hasOwnProperty.call(obj, 'toString') // false
In the above code, hasOwnProperty
it is obj
a method inherited by the object. If this method is overridden, the correct result will not be obtained. call
The method can solve this problem. It hasOwnProperty
puts the original definition of the method obj
on the object and executes it, so whether obj
there is a method with the same name or not, it will not affect the result.
Function.prototype.apply()
apply
The function of the method call
is similar to the method, which is to change this
the pointer and then call the function. The only difference is that it receives an array as a parameter when the function is executed, and the usage format is as follows.
func.apply(thisValue, [arg1, arg2, ...])
apply
The first parameter of the method is also this
the object to be pointed to. If it is set to null
or undefined
, it is equivalent to specifying the global object. The second parameter is an array, and all members of the array are passed as parameters to the original function in turn. The parameters of the original function call
must be added one by one in the method, but in apply
the method, they must be added in the form of an array.
function f(x, y){
console.log(x + y);
}
f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
In the above code, f
the function originally accepts two parameters, apply
but after using the method, it can accept an array as a parameter.
Using this, you can do some interesting applications.
(1) Find the largest element of the array
JavaScript does not provide a function to find the largest element of an array. Combining apply
methods and Math.max
methods, you can return the largest element of an array.
var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 15
(2) Change the empty elements of the array toundefined
Through apply
the method, use Array
the constructor to turn the empty elements of the array into undefined
.
Array.apply(null, ['a', ,'b'])
// [ 'a', undefined, 'b' ]
The difference between empty elements and undefined
is that the array forEach
method will skip empty elements, but not skip undefined
. Therefore, when traversing the inner elements, you will get different results.
var a = ['a', , 'b'];
function print(i) {
console.log(i);
}
a.forEach(print)
// a
// b
Array.apply(null, a).forEach(print)
// a
// undefined
// b
(3) Converting array-like objects
In addition, using slice
the methods of array objects, an array-like object (such as arguments
an object) can be converted into a real array.
Array.prototype.slice.apply({
0: 1, length: 1}) // [1]
Array.prototype.slice.apply({
0: 1}) // []
Array.prototype.slice.apply({
0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({
length: 1}) // [undefined]
The parameters of the method in the above code apply
are all objects, but the return results are all arrays, which serves the purpose of converting objects into arrays. As can be seen from the above code, the prerequisite for this method to work is that the processed object must have length
attributes and corresponding numeric keys.
(4) The object that binds the callback function
The previous button click event example can be rewritten as follows.
var o = new Object();
o.f = function () {
console.log(this === o);
}
var f = function (){
o.f.apply(o);
// 或者 o.f.call(o);
};
// jQuery 的写法
$('#button').on('click', f);
In the above code, after clicking the button, the console will be displayed true
. Since apply()
the method (or call()
method) not only binds the object where the function is executed, but also executes the function immediately, the binding statement has to be written in a function body. A more concise way of writing is to use the method described below bind()
.
Function.prototype.bind()
bind()
Methods are used to bind a function body this
to an object and then return a new function.
var d = new Date();
d.getTime() // 1481869925657
var print = d.getTime;
print() // Uncaught TypeError: this is not a Date object.
In the above code, we d.getTime()
assign the method to a variable print
, and then call it print()
and report an error. This is because getTime()
inside the method this
, Date
the instance of the bound object is assigned to the variable print
, and the inside this
no longer points to Date
the instance of the object.
bind()
method can solve this problem.
var print = d.getTime.bind(d);
print() // 1481869925657
In the above code, bind()
the method binds getTime()
the internals of the method this
to d
the object, and then this method can be safely assigned to other variables.
bind
The parameter of the method is this
the object to be bound, the following is a clearer example.
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.bind(counter);
func();
counter.count // 1
In the code above, counter.inc()
methods are assigned to variables func
. bind()
At this time, a method must be used to bind inc()
the internal , otherwise an error will occur.this
counter
this
Binding to other objects is also possible.
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var obj = {
count: 100
};
var func = counter.inc.bind(obj);
func();
obj.count // 101
In the above code, bind()
the method binds inc()
the internal of the method this
to obj
the object. As a result, after calling func
the function, what is incremented is obj
the internal count
property.
bind()
It can also accept more parameters, and bind these parameters to the parameters of the original function.
var add = function (x, y) {
return x * this.m + y * this.n;
}
var obj = {
m: 2,
n: 2
};
var newAdd = add.bind(obj, 5);
newAdd(5) // 20
In the above code, bind()
in addition to binding the object, the method this
also binds add()
the first parameter of the function to , and then returns a new function , which can run as long as it accepts one more parameter.x
5
newAdd()
y
If bind()
the first parameter of the method is null
or undefined
, equals will be this
bound to the global object, and the function this
will point to the top-level object (for browsers window
) when it runs.
function add(x, y) {
return x + y;
}
var plus5 = add.bind(null, 5);
plus5(10) // 15
In the above code, add()
there is no inside the function. The main purpose of this
using the method is to bind parameters . Every time you run a new function in the future , you only need to provide another parameter . And because there is no inside , so the first parameter is , but if it is another object here, it has no effect.bind()
x
plus5()
y
add()
this
bind()
null
bind()
There are some precautions for using the method.
(1) Return a new function each time
bind()
Every time the method is run, it returns a new function, which creates some problems. For example, when listening to events, it cannot be written as follows.
element.addEventListener('click', o.m.bind(o));
In the above code, an anonymous function generated by click
the event binding method. bind()
This makes it impossible to unbind, so the following code is invalid.
element.removeEventListener('click', o.m.bind(o));
The correct way is to write it like this:
var listener = o.m.bind(o);
element.addEventListener('click', listener);
// ...
element.removeEventListener('click', listener);
(2) Combined with the callback function
Callbacks are one of the most common patterns in JavaScript, but a common mistake is to this
treat contained methods directly as callbacks. The solution is to use bind()
methods that will counter.inc()
bind counter
.
var counter = {
count: 0,
inc: function () {
'use strict';
this.count++;
}
};
function callIt(callback) {
callback();
}
callIt(counter.inc.bind(counter));
counter.count // 1
In the above code, callIt()
the method calls the callback function. At this time, if you pass in directly , the internal one will point to the global object counter.inc
when calling . After using the method to bind , you won't have this problem, always pointing to .counter.inc()
this
bind()
counter.inc
counter
this
counter
There is another situation that is more subtle, that is, some array methods can accept a function as a parameter. The internal this
pointers of these functions are likely to be wrong.
var obj = {
name: '张三',
times: [1, 2, 3],
print: function () {
this.times.forEach(function (n) {
console.log(this.name);
});
}
};
obj.print()
// 没有任何输出
In the above code, the obj.print
internal is pointing , there is no problem with this. However, the callback function of the method points to the global object, so there is no way to get the value. It can be seen more clearly by changing it a little bit.this.times
this
obj
forEach()
this.name
obj.print = function () {
this.times.forEach(function (n) {
console.log(this === window);
});
};
obj.print()
// true
// true
// true
The solution to this problem is also through bind()
method binding this
.
obj.print = function () {
this.times.forEach(function (n) {
console.log(this.name);
}.bind(this));
};
obj.print()
// 张三
// 张三
// 张三
(3) call()
Use in combination with methods
Using bind()
methods, you can rewrite the usage forms of some JavaScript native methods, taking array slice()
methods as an example.
[1, 2, 3].slice(0, 1) // [1]
// 等同于
Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
In the above code, the array method splits another array slice
from the inside according to the specified start position and end position. [1, 2, 3]
The essence of this is [1, 2, 3]
to call Array.prototype.slice()
a method on it, so you can call
express this process with a method and get the same result.
call()
Methods are essentially calling Function.prototype.call()
methods, so the above expression can be bind()
rewritten using methods.
var slice = Function.prototype.call.bind(Array.prototype.slice);
slice([1, 2, 3], 0, 1) // [1]
The meaning of the above code is that it will Array.prototype.slice
become Function.prototype.call
the object where the method is located, and it will become when it is called Array.prototype.slice.call
. Similar writing can also be used for other array methods.
var push = Function.prototype.call.bind(Array.prototype.push);
var pop = Function.prototype.call.bind(Array.prototype.pop);
var a = [1 ,2 ,3];
push(a, 4)
a // [1, 2, 3, 4]
pop(a)
a // [1, 2, 3]
If you go one step further and Function.prototype.call
bind the method to Function.prototype.bind
the object, it means that bind
the calling form can also be rewritten.
function f() {
console.log(this.v);
}
var o = {
v: 123 };
var bind = Function.prototype.call.bind(Function.prototype.bind);
bind(f, o)() // 123
The meaning of the above code is to Function.prototype.bind
bind the method to Function.prototype.call
it, so bind
the method can be used directly without using it on the function instance.