Directorio de artículos
Objeto de instancia y nuevo comando
cual es el objeto
La programación orientada a objetos (Programación orientada a objetos, abreviada como POO) es actualmente el paradigma de programación principal. Abstrae varias relaciones complejas en el mundo real en objetos individuales y luego completa la simulación del mundo real mediante la división del trabajo y la cooperación entre objetos.
Cada objeto es un centro funcional con una clara división del trabajo y puede completar tareas como recibir información, procesar datos y enviar información. Los objetos se pueden reutilizar y personalizar mediante el mecanismo de herencia. Por lo tanto, la programación orientada a objetos tiene las características de flexibilidad, reutilización del código y alta modularidad, y es fácil de mantener y desarrollar. En comparación con la programación de procedimientos tradicional (programación de procedimientos) que consta de una serie de funciones o instrucciones, es más adecuada para cooperación entre varias personas Grandes proyectos de software.
Entonces, ¿qué es exactamente un "objeto"? Lo entendemos en dos niveles.
(1) Un objeto es una abstracción de un solo objeto.
Un libro, un automóvil y una persona pueden ser objetos, al igual que una base de datos, una página web y una conexión a un servidor remoto. Cuando los objetos se abstraen en objetos, la relación entre objetos se convierte en la relación entre objetos, de modo que se puede simular la situación real y se puede realizar la programación de objetos.
(2) Un objeto es un contenedor que encapsula propiedades y métodos.
Las propiedades son el estado del objeto y los métodos son el comportamiento del objeto (realizar algún tipo de tarea). Por ejemplo, podemos abstraer un animal en animal
un objeto, usar "atributos" para registrar qué animal es y usar "métodos" para representar ciertos comportamientos de los animales (correr, cazar, descansar, etc.).
Constructor
El primer paso en la programación orientada a objetos es generar objetos. Como se mencionó anteriormente, un objeto es una abstracción de una sola entidad. Por lo general, se necesita una plantilla para representar las características comunes de un determinado tipo de objeto, y luego el objeto se genera de acuerdo con esta plantilla.
Los lenguajes de programación típicos orientados a objetos (como C++ y Java) tienen el concepto de "clase". La llamada "clase" es la plantilla del objeto y el objeto es la instancia de la "clase". Sin embargo, el sistema de objetos del lenguaje JavaScript no se basa en "clases", sino en constructores y cadenas de prototipos.
El lenguaje JavaScript utiliza constructores como plantillas para objetos. El llamado "constructor" es una función especialmente utilizada para generar objetos de instancia. Es la plantilla del objeto, que describe la estructura básica del objeto de instancia. Un constructor puede generar múltiples objetos de instancia, todos los cuales tienen la misma estructura.
Un constructor es simplemente una función ordinaria, pero con sus propias características y uso.
var Vehicle = function () {
this.price = 1000;
};
En el código anterior, Vehicle
es el constructor. Para distinguirlo de las funciones ordinarias, la primera letra del nombre del constructor suele estar en mayúscula.
Hay dos características del constructor.
- La palabra clave se utiliza dentro del cuerpo de la función
this
, que representa la instancia del objeto que se generará. - Al generar objetos,
new
se deben utilizar comandos.
El constructor básicamente no devuelve un valor.
Los comandos se introducen primero new
.
nuevo comando
uso básico
new
La función del comando es ejecutar el constructor y devolver un objeto de instancia.
var Vehicle = function () {
this.price = 1000;
};
var v = new Vehicle();
v.price // 1000
El código anterior usa new
el comando para permitir que el constructor Vehicle
genere un objeto de instancia y lo guarde en una variable v
. Este objeto de instancia recién generado Vehicle
obtiene price
propiedades del constructor. new
Cuando se ejecuta el comando, el interior del constructor this
representa el objeto de instancia recién generado, this.price
lo que indica que el objeto de instancia tiene un price
atributo con un valor de 1000.
Cuando se utilizan new
comandos, el constructor también puede aceptar parámetros según sea necesario.
var Vehicle = function (p) {
this.price = p;
};
var v = new Vehicle(500);
v.price // 500
new
El comando en sí puede ejecutar el constructor, por lo que el siguiente constructor puede tener paréntesis o no. Las siguientes dos líneas de código son equivalentes, pero para indicar que se trata de una llamada a función, se recomiendan paréntesis.
// 推荐的写法
var v = new Vehicle();
// 不推荐的写法
var v = new Vehicle;
Una pregunta natural es: ¿ new
qué sucede si olvidas usar el comando y simplemente llamas al constructor?
En este caso, el constructor se convierte en una función ordinaria y no genera un objeto de instancia. Y debido a las razones que se mencionarán más adelante, this
representar el objeto global en este momento provocará algunos resultados inesperados.
var Vehicle = function (){
this.price = 1000;
};
var v = Vehicle();
v // undefined
price // 1000
En el código anterior, Vehicle
al llamar al constructor, olvidé agregar new
el comando. Como resultado, dado que la función Vehículo no tiene valor de retorno, la variable v
y undefined
la price
propiedad se convierten en una variable global . new
Por lo tanto, se debe tener mucho cuidado para evitar llamadas directas a constructores sin un comando.
Para garantizar que el constructor deba new
usarse con el comando, una solución es usar el modo estricto dentro del constructor, es decir, agregar la primera línea use strict
. En este caso, una vez que olvide usar new
el comando, llamar directamente al constructor informará un error.
function Fubar(foo, bar){
'use strict';
this._foo = foo;
this._bar = bar;
}
Fubar()
// TypeError: Cannot set property '_foo' of undefined
El código anterior Fubar
es un constructor y use strict
el comando garantiza que la función se ejecute en modo estricto. Porque en modo estricto, el this
objeto interno de la función no puede apuntar al objeto global, que es igual a por defecto undefined
, y se informará un error si no se new
llama (JavaScript no permite undefined
agregar atributos).
Otra solución es juzgar si se debe usar el comando dentro del constructor new
y devolver un objeto de instancia directamente si se descubre que no se usa.
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
El constructor en el código anterior new
obtendrá el mismo resultado sin importar si el comando se agrega o no.
El principio del nuevo mando.
Cuando se utiliza un comando new
, la función que lo sigue ejecuta los siguientes pasos en secuencia.
- Cree un objeto vacío como instancia del objeto que se devolverá.
- Apunte el prototipo de este objeto vacío a las propiedades del constructor
prototype
. - Asigne este objeto vacío a
this
la palabra clave dentro de la función. - Comience a ejecutar el código dentro del constructor.
En otras palabras, dentro del constructor, this
se refiere a un objeto vacío recién generado y todas this
las operaciones específicas se realizarán en este objeto vacío. La razón por la que un constructor se llama "constructor" significa que el propósito de esta función es manipular un objeto vacío (es decir, this
un objeto) y "construirlo" en lo que necesita.
Si hay una declaración dentro del constructor return
y return
va seguida de un objeto, new
el comando devolverá return
el objeto especificado por la declaración; de lo contrario, devolverá return
el this
objeto independientemente de la declaración.
var Vehicle = function () {
this.price = 1000;
return 1000;
};
(new Vehicle()) === 1000
// false
En el código anterior, la Vehicle
declaración del constructor return
devuelve un valor. En este momento, new
el comando ignorará esta return
declaración y devolverá el this
objeto "construido".
Sin embargo, si return
la declaración devuelve un this
objeto nuevo no relacionado, new
el comando devuelve el objeto nuevo en lugar this
del objeto. Este punto necesita especial atención.
var Vehicle = function (){
this.price = 1000;
return {
price: 2000 };
};
(new Vehicle()).price
// 2000
Vehicle
En el código anterior, la declaración del constructor return
devuelve un nuevo objeto. new
El comando devuelve este objeto, no this
el objeto.
Por otro lado, si el comando this
se usa en una función normal (una función sin palabras clave dentro) new
, devolverá un objeto vacío.
function getMessage() {
return 'this is a message';
}
var msg = new getMessage();
msg // {}
typeof msg // "object"
En el código anterior, getMessage
es una función ordinaria que devuelve una cadena. Al usar new
comandos en él, obtienes un objeto vacío. Esto se debe a que new
los comandos siempre devuelven un objeto, ya sea el objeto de instancia o return
el objeto especificado por la declaración. En este caso, return
la declaración devuelve una cadena, por lo que new
el comando ignora la declaración.
new
El proceso interno simplificado del comando se puede representar mediante el siguiente código.
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);
esta palabra clave
significado
this
Las palabras clave son un punto gramatical muy importante. No es exagerado decir que la mayoría de las tareas de desarrollo no pueden llevarse a cabo sin comprender sus implicaciones.
Como se mencionó en el capítulo anterior, this
se puede utilizar en constructores para representar objetos de instancia. Además, this
también se puede utilizar en otras ocasiones. Pero no importa cuál sea la ocasión, this
hay una cosa en común: siempre devuelve un objeto.
En pocas palabras, this
es el objeto donde reside la propiedad o método "actualmente".
this.property
En el código anterior, this
representa property
el objeto donde se encuentra actualmente el atributo.
A continuación se muestra un ejemplo práctico.
var person = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
person.describe()
// "姓名:张三"
En el código anterior, this.name
significa name
el objeto donde se encuentra el atributo. Dado que this.name
se describe
llama en un método y describe
el objeto actual donde se encuentra el método es person
, this
apunta a person
, this.name
es decir person.name
.
Dado que las propiedades de un objeto se pueden asignar a otro objeto, el objeto actual donde se encuentra la propiedad es mutable, es decir, el this
puntero a es mutable.
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe()
// "姓名:李四"
En el código anterior, A.describe
el atributo está asignado a B
, por lo que B.describe
significa que describe
el objeto actual donde se encuentra el método es B
, por lo que this.name
apunta a B.name
.
Al refactorizar un poco este ejemplo, this
el apuntamiento dinámico de 's se puede ver más claramente.
function f() {
return '姓名:'+ this.name;
}
var A = {
name: '张三',
describe: f
};
var B = {
name: '李四',
describe: f
};
A.describe() // "姓名:张三"
B.describe() // "姓名:李四"
En el código anterior, las palabras clave f
se utilizan dentro de la función this
y los punteros son diferentes según f
el objeto en el que se encuentra this
.
Siempre que la función esté asignada a otra variable, this
el puntero cambiará.
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var name = '李四';
var f = A.describe;
f() // "姓名:李四"
En el código anterior, A.describe
si se asigna a una variable f
, la interna this
apuntará al f
objeto donde se está ejecutando (en este caso, el objeto de nivel superior).
Veamos otro ejemplo de programación web.
<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>
El código anterior es un cuadro de entrada de texto. Cada vez que el usuario ingresa un valor, onChange
se llamará a la función de devolución de llamada para verificar si el valor está dentro del rango especificado. El navegador pasará el objeto actual a la función de devolución de llamada, por lo que this
significa pasar el objeto actual (es decir, el cuadro de texto), y luego se this.value
podrá leer el valor de entrada del usuario.
En resumen, en el lenguaje JavaScript, todo es un objeto y el entorno operativo también es un objeto, por lo que las funciones se ejecutan en un objeto, que es this
el objeto (entorno) donde se ejecuta la función. Esto no confundirá a los usuarios, pero JavaScript admite el cambio dinámico del entorno de ejecución, es decir, this
el puntero es dinámico y no hay forma de determinar de antemano a qué objeto apuntar, lo que es lo que más confunde a los principiantes.
sustancia
La razón por la que el lenguaje JavaScript tiene este diseño está relacionada con la estructura de datos en la memoria.
var obj = {
foo: 5 };
El código anterior asigna un objeto a una variable obj
. El motor JavaScript primero generará un objeto en la memoria { foo: 5 }
y luego asignará la dirección de memoria del objeto a una variable obj
. En otras palabras, una variable obj
es una dirección (referencia). Si desea leer más tarde obj.foo
, el motor primero obj
obtiene la dirección de memoria, luego lee el objeto original de la dirección y devuelve sus foo
atributos.
El objeto original se almacena en una estructura de diccionario y cada nombre de atributo corresponde a un objeto de descripción de atributo. Por ejemplo, foo
las propiedades del ejemplo anterior en realidad se guardan en el siguiente formulario.
{
foo: {
[[value]]: 5
[[writable]]: true
[[enumerable]]: true
[[configurable]]: true
}
}
Tenga en cuenta que foo
el valor del atributo se almacena en value
el atributo del objeto de descripción del atributo.
Esta estructura es muy clara: el problema es que el valor del atributo puede ser una función.
var obj = {
foo: function () {
} };
En este momento, el motor guardará la función en la memoria por separado y luego asignará la dirección de la función al atributo foo
del value
atributo.
{
foo: {
[[value]]: 函数的地址
...
}
}
Dado que una función es un valor único, se puede ejecutar en diferentes entornos (contextos).
var f = function () {
};
var obj = {
f: f };
// 单独执行
f()
// obj 环境执行
obj.f()
JavaScript permite referencias a otras variables del entorno actual dentro del cuerpo de una función.
var f = function () {
console.log(x);
};
En el código anterior, las variables se utilizan en el cuerpo de la función x
. Esta variable la proporciona el entorno de ejecución.
Ahora surge el problema, dado que la función se puede ejecutar en diferentes entornos operativos, es necesario que haya un mecanismo para obtener el entorno operativo actual (contexto) dentro del cuerpo de la función. Por lo tanto, this
apareció y su propósito de diseño es hacer referencia al entorno operativo actual de la función dentro del cuerpo de la función.
var f = function () {
console.log(this.x);
}
En el código anterior, el cuerpo de la función this.x
se refiere al entorno operativo actual x
.
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 单独执行
f() // 1
// obj 环境执行
obj.f() // 2
En el código anterior, la función f
se ejecuta en el entorno global, this.x
apuntando al entorno global ; se ejecuta x
en el entorno, apuntando a .obj
this.x
obj.x
ocasiones de uso
this
Existen principalmente las siguientes ocasiones de uso.
(1) Entorno global
Utilizado por el entorno global this
, se refiere al objeto de nivel superior window
.
this === window // true
function f() {
console.log(this === window);
}
f() // true
El código anterior muestra que, ya sea que esté dentro de una función o no, siempre que se ejecute en el entorno global, this
se refiere al objeto de nivel superior window
.
(2) Constructor
En el constructor this
, se refiere al objeto de instancia.
var Obj = function (p) {
this.p = p;
};
El código anterior define un constructor Obj
. Dado que this
apunta al objeto de instancia, se define dentro del constructor this.p
, lo que equivale a definir un p
atributo del objeto de instancia.
var o = new Obj('Hello World!');
o.p // "Hello World!"
(3) Método de objeto
Si el método del objeto contiene this
, this
el puntero es el objeto donde se ejecuta el método. Asignar este método a otro objeto cambiará this
el puntero.
Sin embargo, esta regla no es fácil de entender. Consulte el código a continuación.
var obj ={
foo: function () {
console.log(this);
}
};
obj.foo() // obj
En el código anterior, obj.foo
cuando se ejecuta el método, su this
puntero interno obj
.
Sin embargo, los siguientes usos cambiarán this
la dirección.
// 情况一
(obj.foo = obj.foo)() // window
// 情况二
(false || obj.foo)() // window
// 情况三
(1, obj.foo)() // window
En el código anterior, obj.foo
es un valor. Cuando realmente se llama a este valor, el entorno operativo ya no es obj
, sino el entorno global, por lo que this
ya no apunta a él obj
.
Se puede entender que dentro del motor JavaScript obj
y obj.foo
almacenadas en dos direcciones de memoria, llamadas dirección uno y dirección dos. obj.foo()
Al llamar de esta manera, la dirección 2 se llama desde la dirección 1, por lo que el entorno operativo de la dirección 2 es la dirección 1, que this
apunta a obj
. Sin embargo, en los tres casos anteriores, se saca y llama directamente a la dirección 2. En este caso, el entorno operativo es el entorno global, por lo que apunta this
al entorno global. Los tres casos anteriores son equivalentes al código siguiente.
// 情况一
(obj.foo = function () {
console.log(this);
})()
// 等同于
(function () {
console.log(this);
})()
// 情况二
(false || function () {
console.log(this);
})()
// 情况三
(1, function () {
console.log(this);
})()
Si this
el método no está en la primera capa del objeto, this
solo apunta al objeto de la capa actual y no hereda la capa superior.
var a = {
p: 'Hello',
b: {
m: function() {
console.log(this.p);
}
}
};
a.b.m() // undefined
En el código anterior, a.b.m
el método está en a
la segunda capa del objeto, y el interior del método this
no apunta a
, sino apunta a.b
, porque la ejecución real es el siguiente código.
var b = {
m: function() {
console.log(this.p);
}
};
var a = {
p: 'Hello',
b: b
};
(a.b).m() // 等同于 b.m()
Si desea lograr el efecto deseado, solo puede escribirlo de la siguiente manera.
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
Si asigna el método dentro del objeto anidado a una variable en este momento, seguirá this
apuntando al objeto global.
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
var hello = a.b.m;
hello() // undefined
En el código anterior, m
es un método dentro del objeto multicapa. Para simplificar, se asigna a hello
una variable y, cuando se llama al resultado, this
apunta al objeto de nivel superior. Para evitar este problema, solo puedes m
asignar el objeto donde se encuentra hello
, de modo que cuando lo llames, this
el puntero al mismo no cambie.
var hello = a.b;
hello.m() // Hello
Puntos a tener en cuenta
Evite múltiples capas de esto
Dado que this
el puntero a es indeterminado, nunca incluya varias capas en una función this
.
var o = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this);
}();
}
}
o.f1()
// Object
// Window
El código anterior contiene dos capas this
. Después de la ejecución, la primera capa apunta al objeto o
y la segunda capa apunta al objeto global, porque la ejecución real es el siguiente código.
var temp = function () {
console.log(this);
};
var o = {
f1: function () {
console.log(this);
var f2 = temp();
}
}
Una solución alternativa es utilizar una this
variable en el segundo nivel que apunte al nivel exterior.
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
// Object
// Object
El código anterior define una variable that
, que se fija en la capa exterior this
y luego se usa en la capa interior , por lo que el apuntamiento that
no cambiará.this
this
De hecho, es una práctica muy común usar un valor fijo de una variable y luego llamar a esta variable en la función interna, asegúrese de dominarlo.
JavaScript proporciona un modo estricto, que también puede evitar este tipo de problemas. En modo estricto, si el objeto dentro de la función this
apunta al objeto de nivel superior, se informará un error.
var counter = {
count: 0
};
counter.inc = function () {
'use strict';
this.count++
};
var f = counter.inc;
f()
// TypeError: Cannot read property 'count' of undefined
En el código anterior, el método adopta el modo estricto inc
a través de la declaración. En este momento , una vez que el objeto interno apunta al objeto de nivel superior, se informará un error.'use strict'
this
Evite esto en los métodos de manejo de matrices.
Los métodos map
y de la matriz foreach
permiten que se proporcione una función como argumento. Esta función no debe usarse internamente 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
En el código anterior, foreach
la función de devolución de llamada del método this
en realidad apunta al window
objeto, por lo que o.v
no se puede obtener el valor. this
La razón es la misma que la multicapa del párrafo anterior , es decir, la capa interior this
no apunta al exterior, sino al objeto de nivel superior.
Una forma de resolver este problema, como se mencionó anteriormente, es utilizar una fijación de variable intermedia 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
Otra forma es fijar su entorno de ejecución como segundo parámetro del método 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
Evite esto en la función de devolución de llamada.
Las devoluciones de llamadas this
a menudo cambian los indicadores y es mejor evitarlas.
var o = new Object();
o.f = function () {
console.log(this === o);
}
// jQuery 的写法
$('#button').on('click', o.f);
En el código anterior, después de hacer clic en el botón, se mostrará la consola false
. La razón es que esta vez this
ya no apunta al o
objeto, sino al objeto DOM del botón, porque f
el método se llama en el contexto del objeto del botón. Esta sutil diferencia se pasa por alto fácilmente en la programación, lo que genera errores difíciles de detectar.
Para resolver este problema, se pueden utilizar los siguientes métodos para this
vincular el objeto, es decir, fijarlo this
a un determinado objeto y reducir la incertidumbre.
El método que une este
this
El cambio dinámico de , por supuesto, crea una gran flexibilidad para JavaScript, pero también hace que la programación sea difícil y ambigua. En ocasiones, es necesario this
arreglarlo para evitar situaciones inesperadas. JavaScript proporciona call
estos tres apply
métodos bind
para cambiar/arreglar this
el apuntamiento.
Función.prototipo.call()
Para el método de una instancia de función call
, puede especificar el this
puntero interno de la función (es decir, el alcance donde se ejecuta la función) y luego llamar a la función en el alcance especificado.
var obj = {
};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
En el código anterior, f
cuando la función se ejecuta en el entorno global, this
apunta al entorno global (el navegador es window
un objeto); call
el método puede cambiar this
el apuntamiento, especificar this
el objeto apuntador obj
y luego obj
ejecutar la función en el alcance de el objeto f
.
call
El parámetro del método, que debería ser un objeto. Si el parámetro está vacío null
y undefined
, el objeto global se pasa de forma predeterminada.
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
En el código anterior, si la palabra clave a
en la función apunta al objeto global, el resultado devuelto es . Si el método se utiliza para apuntar la palabra clave a un objeto, el resultado devuelto es . Se puede ver que si el método no tiene parámetros, o el parámetro es o , equivale a apuntar al objeto global.this
123
call
this
obj
456
call
null
undefined
Si call
el parámetro del método es un valor original, el valor original se convertirá automáticamente en el objeto contenedor correspondiente y luego se pasará al call
método.
var f = function () {
return this;
};
f.call(5)
// Number {[[PrimitiveValue]]: 5}
En el código anterior, call
el parámetro 5
no es un objeto, se convertirá automáticamente en un objeto contenedor ( Number
instancia) y se vinculará f
internamente this
.
call
Los métodos también pueden aceptar múltiples parámetros.
func.call(thisValue, arg1, arg2, ...)
call
El primer parámetro de es this
el objeto al que se apuntará y los siguientes parámetros son los parámetros necesarios cuando se llama a la función.
function add(a, b) {
return a + b;
}
add.call(this, 1, 2) // 3
En el código anterior, call
el método especifica el entorno actual vinculante (objeto) add
dentro de la función this
, y los parámetros son 1
y 2
, por lo que add
se obtiene después de ejecutar la función 3
.
call
Una aplicación de los métodos es llamar a métodos nativos en objetos.
var obj = {
};
obj.hasOwnProperty('toString') // false
// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function () {
return true;
};
obj.hasOwnProperty('toString') // true
Object.prototype.hasOwnProperty.call(obj, 'toString') // false
En el código anterior, hasOwnProperty
es obj
un método heredado por el objeto, si se anula este método, no se obtendrá el resultado correcto. call
El método puede resolver este problema: coloca hasOwnProperty
la definición original del método obj
en el objeto y lo ejecuta, por lo que si obj
hay un método con el mismo nombre o no, no afectará el resultado.
Función.prototipo.aplicar()
apply
La función del método call
es similar al método, que consiste en cambiar this
el puntero y luego llamar a la función. La única diferencia es que recibe una matriz como parámetro cuando se ejecuta la función y el formato de uso es el siguiente.
func.apply(thisValue, [arg1, arg2, ...])
apply
El primer parámetro del método también es this
el objeto al que apuntar. Si se establece en null
o undefined
, equivale a especificar el objeto global. El segundo parámetro es una matriz, y todos los miembros de la matriz se pasan como parámetros a la función original por turno. Los parámetros de la función original call
deben agregarse uno por uno en el método, pero en apply
el método deben agregarse en forma de matriz.
function f(x, y){
console.log(x + y);
}
f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
En el código anterior, f
la función originalmente acepta dos parámetros, apply
pero después de usar el método, puede aceptar una matriz como parámetro.
Usando esto, puedes hacer algunas aplicaciones interesantes.
(1) Encuentre el elemento más grande de la matriz
JavaScript no proporciona una función para encontrar el elemento más grande de una matriz. Combinando apply
métodos y Math.max
métodos, puede devolver el elemento más grande de una matriz.
var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 15
(2) Cambie los elementos vacíos de la matriz aundefined
A través apply
del método, use Array
el constructor para convertir los elementos vacíos de la matriz en undefined
.
Array.apply(null, ['a', ,'b'])
// [ 'a', undefined, 'b' ]
La diferencia entre elementos vacíos y undefined
es que el forEach
método de matriz omitirá elementos vacíos, pero no omitirá undefined
. Por lo tanto, al atravesar los elementos internos, obtendrás resultados diferentes.
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) Convertir objetos tipo matriz
Además, utilizando slice
los métodos de los objetos de matriz, un objeto similar a una matriz (como arguments
un objeto) se puede convertir en una matriz real.
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]
Los parámetros del método en el código anterior apply
son todos objetos, pero los resultados devueltos son todos matrices, lo que sirve para convertir objetos en matrices. Como se puede ver en el código anterior, el requisito previo para que este método funcione es que el objeto procesado debe tener length
atributos y claves numéricas correspondientes.
(4) El objeto que vincula la función de devolución de llamada.
El ejemplo anterior del evento de clic en el botón se puede reescribir de la siguiente manera.
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);
En el código anterior, después de hacer clic en el botón, se mostrará la consola true
. Dado que apply()
el método (o call()
método) no solo vincula el objeto donde se ejecuta la función, sino que también ejecuta la función inmediatamente, la declaración vinculante debe escribirse en el cuerpo de una función. Una forma más concisa de escribir es utilizar el método que se describe a continuación bind()
.
Función.prototipo.bind()
bind()
Los métodos se utilizan para vincular el cuerpo de una función this
a un objeto y luego devolver una nueva función.
var d = new Date();
d.getTime() // 1481869925657
var print = d.getTime;
print() // Uncaught TypeError: this is not a Date object.
En el código anterior, d.getTime()
asignamos el método a una variable print
, luego lo llamamos print()
e informamos un error. Esto se debe a que getTime()
dentro del método this
, Date
la instancia del objeto vinculado se asigna a la variable print
y el interior this
ya no apunta a Date
la instancia del objeto.
bind()
El método puede resolver este problema.
var print = d.getTime.bind(d);
print() // 1481869925657
En el código anterior, bind()
el método vincula getTime()
las partes internas del método this
al d
objeto y luego este método se puede asignar de forma segura a otras variables.
bind
El parámetro del método es this
el objeto a vincular, el siguiente es un ejemplo más claro.
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.bind(counter);
func();
counter.count // 1
En el código anterior, counter.inc()
los métodos se asignan a variables func
. En este momento, se debe utilizar bind()
un método para vincular inc()
el interno ; de lo contrario, se producirá un error.this
counter
this
También es posible vincularse a otros objetos.
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var obj = {
count: 100
};
var func = counter.inc.bind(obj);
func();
obj.count // 101
En el código anterior, bind()
el método vincula inc()
el interior del método this
al obj
objeto. Como resultado, después de llamar func
a la función, lo que se incrementa es la propiedad obj
interna .count
bind()
También puede aceptar más parámetros y vincularlos a los parámetros de la función original.
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
En el código anterior, bind()
además de vincular el objeto, el método this
también vincula add()
el primer parámetro de la función y luego devuelve una nueva función , que puede ejecutarse siempre que acepte un parámetro más.x
5
newAdd()
y
Si bind()
el primer parámetro del método es null
o undefined
, igual estará this
vinculado al objeto global y la función this
apuntará al objeto de nivel superior (para navegadores window
) cuando se ejecute.
function add(x, y) {
return x + y;
}
var plus5 = add.bind(null, 5);
plus5(10) // 15
En el código anterior, add()
no hay ninguna función interna . El propósito principal de this
usar el método es vincular parámetros . Cada vez que ejecute una nueva función en el futuro , solo necesita proporcionar otro parámetro . Y como no hay inside , el primer parámetro es , pero si es otro objeto aquí, no tiene ningún efecto.bind()
x
plus5()
y
add()
this
bind()
null
bind()
Existen algunas precauciones para utilizar el método.
(1) Devolver una nueva función cada vez
bind()
Cada vez que se ejecuta el método, devuelve una nueva función, lo que crea algunos problemas. Por ejemplo, al escuchar eventos, no se puede escribir de la siguiente manera.
element.addEventListener('click', o.m.bind(o));
En el código anterior, una función anónima generada por click
el método de enlace de eventos . bind()
Esto hace que sea imposible desvincular, por lo que el siguiente código no es válido.
element.removeEventListener('click', o.m.bind(o));
La forma correcta es escribirlo así:
var listener = o.m.bind(o);
element.addEventListener('click', listener);
// ...
element.removeEventListener('click', listener);
(2) Combinado con la función de devolución de llamada
Las devoluciones de llamada son uno de los patrones más comunes en JavaScript, pero un error común es this
tratar los métodos contenidos directamente como devoluciones de llamada. La solución es utilizar bind()
métodos que se counter.inc()
unan counter
.
var counter = {
count: 0,
inc: function () {
'use strict';
this.count++;
}
};
function callIt(callback) {
callback();
}
callIt(counter.inc.bind(counter));
counter.count // 1
En el código anterior, callIt()
el método llama a la función de devolución de llamada. En este momento, si pasa directamente , el interno apuntará al objeto global counter.inc
al llamar . Después de usar el método para vincular , no tendrás este problema, siempre apuntando a .counter.inc()
this
bind()
counter.inc
counter
this
counter
Hay otra situación que es más sutil, es decir, algunos métodos de matriz pueden aceptar una función como parámetro. Es probable que los indicadores internos this
de estas funciones sean incorrectos.
var obj = {
name: '张三',
times: [1, 2, 3],
print: function () {
this.times.forEach(function (n) {
console.log(this.name);
});
}
};
obj.print()
// 没有任何输出
En el código anterior, lo obj.print
interno apunta , no hay ningún problema con esto. Sin embargo, la función de devolución de llamada del método apunta al objeto global, por lo que no hay forma de obtener el valor. Se puede ver más claramente cambiándolo un poco.this.times
this
obj
forEach()
this.name
obj.print = function () {
this.times.forEach(function (n) {
console.log(this === window);
});
};
obj.print()
// true
// true
// true
La solución a este problema también es mediante bind()
la vinculación de métodos this
.
obj.print = function () {
this.times.forEach(function (n) {
console.log(this.name);
}.bind(this));
};
obj.print()
// 张三
// 张三
// 张三
(3) call()
Uso en combinación con métodos
Usando bind()
métodos, puede reescribir las formas de uso de algunos métodos nativos de JavaScript, tomando slice()
los métodos de matriz como ejemplo.
[1, 2, 3].slice(0, 1) // [1]
// 等同于
Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
En el código anterior, el método de matriz divide otra matriz slice
desde el interior de acuerdo con la posición inicial y final especificadas. [1, 2, 3]
La esencia de esto es [1, 2, 3]
llamar Array.prototype.slice()
a un método, para que pueda call
expresar este proceso con un método y obtener el mismo resultado.
call()
Los métodos son esencialmente Function.prototype.call()
métodos de llamada, por lo que la expresión anterior se puede bind()
reescribir usando métodos.
var slice = Function.prototype.call.bind(Array.prototype.slice);
slice([1, 2, 3], 0, 1) // [1]
El significado del código anterior es que se convertirá Array.prototype.slice
en Function.prototype.call
el objeto donde se encuentra el método y se convertirá en cuando se llame Array.prototype.slice.call
. También se puede utilizar una escritura similar para otros métodos de matriz.
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]
Si va un paso más allá y Function.prototype.call
vincula el método al Function.prototype.bind
objeto, significa que bind
el formulario de llamada también se puede reescribir.
function f() {
console.log(this.v);
}
var o = {
v: 123 };
var bind = Function.prototype.call.bind(Function.prototype.bind);
bind(f, o)() // 123
El significado del código anterior es vincular Function.prototype.bind
el método a Function.prototype.call
él, por lo que bind
el método se puede usar directamente sin usarlo en la instancia de la función.