Network Security Note 2 - new, this in js

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 animalan 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, Vehicleit 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, newcommands must be used.

The constructor basically does not return a value

The commands are introduced first new.

new command

basic usage

newThe 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 newthe command to let the constructor Vehiclegenerate an instance object and save it in a variable v. This newly generated instance object Vehiclegets priceproperties from the constructor. newWhen the command is executed, the inside of the constructor thisrepresents the newly generated instance object, this.priceindicating that the instance object has an priceattribute with a value of 1000.

When using newcommands, the constructor can also accept parameters as needed.

var Vehicle = function (p) {
    
    
  this.price = p;
};

var v = new Vehicle(500);
v.price // 500

newThe 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, newwhat 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, thisrepresenting 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, Vehiclewhen calling the constructor, I forgot to add newthe command. As a result, since the Vehicle function has no return value, the variable vbecomes undefinedand pricethe property becomes a global variable . newTherefore, great care should be taken to avoid direct calls to constructors without a command.

In order to ensure that the constructor must newbe 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 newthe 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 Fubaris a constructor, and use strictthe command ensures that the function runs in strict mode. Because in strict mode, the internal thisobject 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 newcalled (JavaScript does not allow undefinedadding 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 newwill 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.

  1. Create an empty object as the object instance to be returned.
  2. Point the prototype of this empty object to the properties of the constructor prototype.
  3. Assign this empty object to thisthe keyword inside the function.
  4. Start executing the code inside the constructor.

In other words, inside the constructor, thisit refers to a newly generated empty object, and all targeted thisoperations 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, thisan object) and "construct" it into what it needs.

If there is a statement inside the constructor returnand it returnis followed by an object, newthe command will return returnthe object specified by the statement; otherwise, it will returnreturn the thisobject regardless of the statement.

var Vehicle = function () {
    
    
  this.price = 1000;
  return 1000;
};

(new Vehicle()) === 1000
// false

In the code above, the constructor Vehiclestatement returnreturns a value. At this time, newthe command will ignore this returnstatement and return the "constructed" thisobject.

However, if returnthe statement returns an unrelated thisnew object, newthe command returns the new object instead of thisthe object. This point needs special attention.

var Vehicle = function (){
    
    
  this.price = 1000;
  return {
    
     price: 2000 };
};

(new Vehicle()).price
// 2000

VehicleIn the above code, the statement of the constructor returnreturns a new object. newThe command returns this object, not thisthe object.

On the other hand, if the command thisis 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, getMessageit is an ordinary function that returns a string. Using newcommands on it, you get an empty object. This is because newcommands always return an object, either the instance object or returnthe object specified by the statement. In this case, returnthe statement returns a string, so newthe command ignores the statement.

newThe 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

thisKeywords 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, thisit can be used in constructors to represent instance objects. In addition, thisit can also be used in other occasions. But no matter what the occasion, thisthere is one thing in common: it always returns an object.

Simply put, thisit is the object where the property or method "currently" resides.

this.property

In the above code, thisit represents propertythe 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.nameit means namethe object where the attribute is located. Since this.nameit is describecalled in a method, and describethe current object where the method is located is person, it thispoints to person, this.namethat 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 thispointer 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.describethe attribute is assigned to B, so B.describeit means that describethe current object where the method is located is B, so this.nameit points to B.name.

By refactoring this example a little bit, thisthe 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 fare used inside the function this, and the pointers are different depending on fthe object it is located in this.

As long as the function is assigned to another variable, thisthe pointer to will change.

var A = {
    
    
  name: '张三',
  describe: function () {
    
    
    return '姓名:'+ this.name;
  }
};

var name = '李四';
var f = A.describe;
f() // "姓名:李四"

In the above code, A.describeif it is assigned to a variable f, the internal one thiswill point to fthe 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, onChangethe 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 thisit means passing in the current object (that is, the text box), and then this.valuethe 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 thisis 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, thisthe 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 objis an address (reference). If you want to read later obj.foo, the engine first objgets the memory address, then reads the original object from the address, and returns its fooattributes.

The original object is stored in a dictionary structure, and each attribute name corresponds to an attribute description object. For example, foothe properties of the above example are actually saved in the following form.

{
    
    
  foo: {
    
    
    [[value]]: 5
    [[writable]]: true
    [[enumerable]]: true
    [[configurable]]: true
  }
}

Note that foothe value of the attribute is stored in valuethe 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 fooof valuethe 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, thisit 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.xrefers 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 fis executed in the global environment, this.xpointing to the global environment ; executing xin the environment, pointing to .objthis.xobj.x

occasions of use

thisThere 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, thisit 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 thisit points to the instance object, it is defined inside the constructor this.p, which is equivalent to defining an pattribute 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, thisthe pointer to is the object where the method is running. Assigning this method to another object will change thisthe 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.foowhen the method is executed, its internal thispointer obj.

However, the following usages will change thisthe direction.

// 情况一
(obj.foo = obj.foo)() // window
// 情况二
(false || obj.foo)() // window
// 情况三
(1, obj.foo)() // window

In the above code, obj.fooit is a value. When this value is actually called, the operating environment is no longer obj, but the global environment, so thisit no longer points to it obj.

It can be understood that inside the JavaScript engine, objand obj.foostored 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, thispointing 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 thispoints 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 thisthe method is not in the first layer of the object, it thisonly 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.mthe method is in athe second layer of the object, and the inside of the method thisis 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 thiswill 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, mit is a method inside the multi-layer object. For simplicity, it is assigned to helloa variable, and when the result is called, thisit points to the top-level object. In order to avoid this problem, you can only massign the object where it is located hello, so that when you call it, thisthe pointer to it will not change.

var hello = a.b;
hello.m() // Hello

Points to note

Avoid multiple layers of this

Since thisthe 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 thisvariable 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 thatwill not change.this

thisIn 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 thispoints 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 incthrough 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 mapand foreachmethods 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, foreachthe callback function of the method thisactually points to windowthe object, so o.vthe value cannot be obtained. thisThe reason is the same as the multi-layer in the previous paragraph , that is, the inner layer thisdoes 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 thisoften 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 thisit is no longer pointing to othe object, but to the DOM object of the button, because fthe 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 thisbind the object, that is, to make it thisfixed to a certain object and reduce uncertainty.

The method that binds this

thisThe dynamic switching of , of course, creates great flexibility for JavaScript, but it also makes programming difficult and ambiguous. Sometimes, it is necessary to thisfix it to avoid unexpected situations. JavaScript provides call, apply, bindthese three methods to switch/fix thisthe pointing.

Function.prototype.call()

For the method of a function instance call, you can specify the internal thispointer 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, fwhen the function is run in the global environment, thisit points to the global environment (the browser is windowan object); callthe method can change thisthe pointing, specify thisthe pointing object obj, and then objrun the function in the scope of the object f.

callThe method parameter, which should be an object. If the parameter is empty, nulland 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 ain 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.this123callthisobj456callnullundefined

If callthe parameter of the method is an original value, the original value will be automatically converted into the corresponding wrapper object, and then passed into callthe method.

var f = function () {
    
    
  return this;
};

f.call(5)
// Number {[[PrimitiveValue]]: 5}

In the above code, callthe parameter 5is not an object, it will be automatically converted into a wrapper object ( Numberinstance), and bound finternally this.

callMethods can also accept multiple parameters.

func.call(thisValue, arg1, arg2, ...)

callThe first parameter of is thisthe 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, callthe method specifies the binding current environment (object) addinside the function this, and the parameters are 1and 2, so addit is obtained after the function runs 3.

callOne 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, hasOwnPropertyit is obja method inherited by the object. If this method is overridden, the correct result will not be obtained. callThe method can solve this problem. It hasOwnPropertyputs the original definition of the method objon the object and executes it, so whether objthere is a method with the same name or not, it will not affect the result.

Function.prototype.apply()

applyThe function of the method callis similar to the method, which is to change thisthe 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, ...])

applyThe first parameter of the method is also thisthe object to be pointed to. If it is set to nullor 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 callmust be added one by one in the method, but in applythe 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, fthe function originally accepts two parameters, applybut 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 applymethods and Math.maxmethods, 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 applythe method, use Arraythe 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 undefinedis that the array forEachmethod 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 slicethe methods of array objects, an array-like object (such as argumentsan 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 applyare 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 lengthattributes 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 thisto 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, Datethe instance of the bound object is assigned to the variable print, and the inside thisno longer points to Datethe 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 thisto dthe object, and then this method can be safely assigned to other variables.

bindThe parameter of the method is thisthe 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.thiscounter

thisBinding 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 thisto objthe object. As a result, after calling functhe function, what is incremented is objthe internal countproperty.

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 thisalso 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.x5newAdd()y

If bind()the first parameter of the method is nullor undefined, equals will be thisbound to the global object, and the function thiswill 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 thisusing 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()xplus5()yadd()thisbind()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 clickthe 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 thistreat 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.incwhen calling . After using the method to bind , you won't have this problem, always pointing to .counter.inc()thisbind()counter.inccounterthiscounter

There is another situation that is more subtle, that is, some array methods can accept a function as a parameter. The internal thispointers 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.printinternal 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.timesthisobjforEach()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 slicefrom 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 callexpress 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.slicebecome Function.prototype.callthe 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.callbind the method to Function.prototype.bindthe object, it means that bindthe 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.bindbind the method to Function.prototype.callit, so bindthe method can be used directly without using it on the function instance.

Guess you like

Origin blog.csdn.net/weixin_54986292/article/details/131956353