JavaScript entiende objetos

Una característica distintiva de los lenguajes orientados a objetos es que todos tienen el concepto de clases.
A través de clases, puede crear cualquier cantidad de objetos con las mismas propiedades y métodos.
Un objeto en JavaScript se define como "una colección desordenada de propiedades cuyos valores de propiedad pueden incluir valores, objetos o funciones básicos".

Crear objeto

Patrón constructor de objetos

  • Primero cree un objeto vacío y luego agregue propiedades y métodos dinámicamente
  • Escenario de uso: datos iniciales con contenido de objeto incierto

La forma más sencilla de crear un objeto personalizado es crear una instancia de Objeto y luego agregarle propiedades y métodos.

var person = new Object()
person.name = 'Tom'
person.age = 29
person.sayName = function(){
    
    
    alert(this.name)
}

Los primeros desarrolladores solían utilizar este patrón para crear nuevos objetos.

patrón literal de objeto

:::tip
Ahora los literales de objetos se han convertido en el modo preferido para crear objetos.
En el objeto, este es el objeto mismo
:::

  • Utilice {} para crear un objeto y especificar métodos de atributo
  • Escenario de uso: datos que determinan inicialmente el contenido de un objeto
  • Problema: si crea varios objetos, el código es muy repetitivo
var person = {
    
    
    name:'Tom',
    age:29,
    sayName:function(){
    
    
        alert(this.name)
    }
}

Acceso a las propiedades del objeto.

obj.prop

var person = {
    
    
    name:'Tom',
    age:29,
    sayName:function(){
    
    
        alert(this.name)
    }
}

person.name

obj['prop']

  1. Cuando el nombre del atributo contiene caracteres especiales, se debe acceder a él entre corchetes
    var p={
          
          }
    //p.content-type='text/json'
    p['content-type']='text/json'
    
  2. El nombre de la variable es incierto
    var propName = 'myAge'
    var value = 18
    //p.propName=value
    p[propName]=value
    

Eliminación de atributos de objeto

var person = {
    
    
    name:'Tom',
    age:29,
    sayName:function(){
    
    
        alert(this.name)
    }
}

delete person.name
delete person.sayName

delete person.sayName() //这个是方法的执行不是删除

Tipo de propiedad

ECMAScript tiene dos tipos de atributos: atributos de datos y atributos de acceso.

atributos de datos

Los atributos de datos tienen 4 características que describen su comportamiento.

  • [[Configurable]]: ¿Se puede eliminar y redefinir el atributo mediante eliminación?¿Es configurable?
  • [[Enumerable]]: ¿Se pueden devolver atributos a través de un bucle for-in?
  • [[Writable]]: ¿Se puede modificar el valor del atributo?
  • [[Valor]]: el valor de datos del atributo

Para las propiedades definidas directamente en el objeto como en el ejemplo anterior, sus [[Configurable]], [[Enumerable]], [[Writable]] son ​​todos verdaderos y [[Value]] se establece en el valor especificado (si no especificado, no está definido)

var person = {
    
    
    name:'Tom' //这里创建了一个name属性,[[Value]]值为Tom
}

Para modificar las propiedades predeterminadas de una propiedad, debe utilizar el método Object.defineProperty().
Este método recibe tres parámetros: el objeto donde se encuentra la propiedad, el nombre de la propiedad y un objeto descriptor.
Los atributos del objeto descriptor deben ser: configurable, enumerable, escribible y valor.

var person = {
    
    }
Object.defineProperty(
    person,
    "name",
    {
    
    
        configurable:false,//这个name属性值不可配置
        //configurable为false,表示不能从对象中删除删除属性,不能对它调用delete。而且一旦属性定义为不可配置就不能再把它变回可配置了。此时再调用Object.defineProperty方法修改除writable之外的特性都会导致错误
        //也就是说可以多次调用Object.defineProperty方法修改同一个属性,但当configurable=false后就有了限制
        writable:false,//这个name属性值不可修改
        value:'Tom'
    }
)

propiedades del accesor

La propiedad de acceso no contiene un valor de datos, es un par de funciones getter y setter (aunque estas dos funciones no son necesarias).
Las propiedades de los accesorios tienen las siguientes 4 características:

  • [[Configurable]]: ¿Se puede eliminar y redefinir el atributo mediante eliminación?¿Es configurable?
  • [[Enumerable]]: ¿Se pueden devolver atributos a través de un bucle for-in?
  • [[Get]]: función llamada cuando se leen propiedades por defecto indefinidas
  • [[Set]]: función llamada al escribir un valor predeterminado de propiedad indefinido

Las propiedades del accesor no se pueden definir directamente y deben definirse a través de Object.defineProperty

var book = {
    
    
    _year:2004,
    last:0
}
Object.defineProperty(
    book,
    "year",
    {
    
    
        get:function(){
    
    
            return this._year
        },
        set:function(newValue){
    
    
            this.last = this._year
            this._year = newValue
        }
    }
)
book.year = 2005
console.log(book.year)
//对象_year前面的下划线是一种记号,表示只能通过对象方法访问的属性
//访问器属性year则包含一个getter和一个setter,用于修改对象中的_year
//这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化
//不一定非要同时指定getter和setter
//只知道getter意味属性是不能写的

Object.defineProperty es un método ES5. Antes de este método, para crear propiedades de acceso, generalmente se usan dos métodos no estándar, defineGetter y defineSetter .

var book = {
    
    
    _year:2004,
    last:0
}
book.__defineGetter__("year",function(){
    
    
    return this._year;
})
book.__defineSetter__("year",function(){
    
    
    this.last = this._year
    this._year = newValue
})

Definir múltiples propiedades

ES5 también define un método Object.defineProperties().
Utilice este método para definir varias propiedades a la vez mediante descriptores.

var book = {
    
    }
Object.defineProperties(book,{
    
    
    _year:{
    
    
        value:2004
    },
    last:{
    
    
        value:0
    },
    year:{
    
    
        get:function(){
    
    
            return this._year
        },
        set:function(newValue){
    
    
            this.last = this._year 
            this._year = newValue
        }
    }
})
/*
以上代码在book对外定义了两个数据属性(_year,last)
一个访问器属性(year)
*/

Leer características de atributos

Utilice el método ES5 Object.getOwnPropertyDescriptor() para obtener el descriptor de una propiedad determinada.
Este método recibe dos parámetros, el objeto donde se encuentra la propiedad y el nombre de la propiedad desde la cual se debe leer su descriptor.
El valor de retorno es un objeto, que puede ser un objeto de colección de propiedades de acceso o un objeto de colección de propiedades de datos.

var book = {
    
    }
Object.defineProperties(book,{
    
    
    _year:{
    
    
        value:2004
    },
    last:{
    
    
        value:0
    },
    year:{
    
    
        get:function(){
    
    
            return this._year
        },
        set:function(newValue){
    
    
            this.last = this._year 
            this._year = newValue
        }
    }
})
var desc = Object.getOwnPropertyDescriptor(book,"_year");
console.log(desc.value) //2004
console.log(desc.configurable) //false

var test = {
    
    name:'Tom'}
Object.getOwnPropertyDescriptor(test,'name')
//{value: "Tom", writable: true, enumerable: true, configurable: true}

Optimización de objetos creados.

Aunque el constructor de objetos o el literal de objeto se pueden usar para crear un solo objeto,
estos métodos tienen una desventaja obvia: crear muchos objetos usando la misma interfaz producirá una gran cantidad de código duplicado.
Para resolver este problema, la gente comenzó a utilizar algunas variaciones del patrón de fábrica.

Patrón de fábrica

Dado que las clases no se pueden crear en ES, los desarrolladores inventaron una función para encapsular instancias de objetos creados con una interfaz específica.

function createPerson(name){
    
    
    var o = new Object()
    o.name = name
    o.sayName = function(){
    
    
        alert(this.name)
    }
    return o
}
var person1 = createPerson('Tom')
var person2 = createPerson('Jank')

:::advertencia

  1. Aunque el patrón de fábrica resuelve el problema de crear múltiples objetos familiares,
  2. Pero no resuelve el problema del reconocimiento de objetos (es decir, cómo saber el tipo de un objeto, los objetos que crea no tienen un tipo específico, todos son tipos de objetos)
  3. Entonces surgió un nuevo modo de crear objetos
    :::

patrón constructor

function Person(name){
    
    
    this.name = name
    this.sayName = function(){
    
    
        alert(this.name)
    }
}
var person1 = new Person('Tom')
var person2 = new Person('Jank')

:::consejo

  • Se pueden encontrar algunas características especiales del constructor:
    • Los objetos se crean explícitamente usando new
    • Asigne directamente propiedades y métodos a esto.
    • sin declaración de devolución
    • Los nombres de las funciones están en mayúscula (por convención, los constructores siempre comienzan con una letra mayúscula y las funciones que no son constructoras comienzan con minúsculas. Este enfoque se toma prestado de otros lenguajes OO).
      :::

Mejores prácticas para pasar parámetros de constructor

//1. 对象字面量 直接 创建对象 对象的属性可以修改 添加 删除
var obj ={
    
    
    name:'kitty',
    age:16,
    eat:function(){
    
    
    }
}
delete obj.name
delete obj.eat//不能是delete obj.eat()

//2. 用构造函数创建对象
//与字面量创建一样,没有任何优势
var obj = new Object() 
……

//3. 自定义构造函数
//大驼峰 => 构造函数
function Teacher(name,sex){
    
    
    this.name=name,
    this.sex=sex,
    this.smoke =function(){
    
    
        console.log('I am smoking')
    }
}
/**
函数没执行的时候 this 还没有生成
它是构造函数,不是对象
只有实例化的构造函数才是对象,this指向这个实例化的对象
**/
var teacher1 = new Teacher('张三','男')
var teacher2 = new Teacher('李四','女')
  • Mejores prácticas: pasar parámetros de objetos cuando se utilizan constructores
//3.1使用构造函数时传递对象参数(最佳实战)
function Teacher(obj){
    
    
    this.name=obj.name,
    this.sex=obj.sex,
    this.smoke =function(){
    
    
        console.log('I am smoking')
    }
}
//new vue() 就是按这种方法初始化的,所以封装框架的时候也按此方法
//易于维护,适合框架的配置。属性名和属性值可以一目了然,强烈推荐
//也没有参数顺序的问题
var teacher1 = new Teacher({
    
    
    name:'张三',
    sex:'男'
})
//也可以在html中配置,把对象字符串解析为对象
<div data-config='{name:"张三"}'></div>

nuevo (este) principio de creación de instancias operador-constructor

:::sugerencia nueva función

  1. Crear un nuevo objeto
  2. Asigne el alcance del constructor al nuevo objeto (para que esto apunte al nuevo objeto)
  3. Ejecutar código en constructor
  4. Devolver nuevo objeto
    :::
function Car(color,brand){
    
    
    // 1. this = {}  不用this 照样可以
    // 2. 执行代码
    this.color=color
    this.brand = brand
    // 3. 函数最后有一句隐式的 return this 
}
var car = new Car('red','Mazda')

// ==============
function Car(color,brand){
    
    
    var me = {
    
    }
    me.color=color
    me.brand = brand
    return me //显式 return
}
var car = new Car('red','Mazda')

//总结:new的时候 把函数里面指向window的this,转换为指向为实例化的对象

:::advertencia

  1. Si el valor de retorno es un valor que no es de referencia, no lo reconocerá y aún así lo devolverá implícitamente.
  2. Si se devuelve un valor de referencia, el valor de referencia se devuelve correctamente
    :::
function Car(color,brand){
    
    
    this.color=color
    this.brand = brand
    return '123'
    //如果return的是非引用值,它不会识别 仍然隐式返回 this
    //如果return 引用值 ,则正确返回
}
var car = new Car('red','Mazda')//123

Atributos del constructor constructor e instancia de

Las instancias de objetos del mismo tipo tienen un atributo de constructor, que apunta al constructor.

person1.constructor == Person//true
person2.constructor == Person//true

La propiedad constructora de un objeto se utiliza inicialmente para identificar el tipo de objeto.
Pero para detectar tipos de objetos, el operador instancia de es más confiable.
Los objetos creados anteriormente son todas instancias de Objeto y
también son instancias de Persona.

person1 instanceof Object // true
person1 instanceof Person // true

Crear un constructor personalizado significa que futuras instancias del mismo se pueden identificar como un tipo específico.
Y aquí es donde el constructor supera el patrón de fábrica.

Trate a los constructores como funciones.

La única diferencia entre un constructor y otras funciones es la forma en que se llama.
Siempre que se llame a cualquier función a través del operador nuevo, se puede utilizar como constructor.
Si no se llama a alguna función a través del operador nuevo, entonces no es diferente de una función ordinaria.

//-----------
function Car(color,brand){
    
    
    //如果前面有new ,this = {} 指向当前实例
    this.color=color
    this.brand = brand
    //函数最后有一句隐式的 return this
}

Car()     //执行时 this 指向window
new Car() //执行时 this 指向new出来的实例变量

problema de constructor

//-----------
function Person(name){
    
    
    this.name = name
    this.sayName = new Function("alert(this.name)")
    //与声明函数在逻辑上是等价的
}

Mirando el constructor desde esta perspectiva, encontramos que la instancia de Función de cada instancia de constructor es diferente.
Por lo tanto, las funciones con el mismo nombre en diferentes instancias no son iguales
, pero las funciones con el mismo nombre en cada instancia realizan la misma función, por lo que no es necesario crear más.

person1.sayName == person2.sayName //falses

Resuelva este problema moviendo la definición de la función fuera del constructor

//-----------
function Person(name){
    
    
    this.name = name
    this.sayName = sayName
    //与声明函数在逻辑上是等价的
}

function sayName(){
    
    
    alert(this.name)
}

De hecho, esto puede resolver el problema de recrear la función de la instancia del objeto con el mismo nombre cada vez.
Pero vuelve a surgir un nuevo problema:
las funciones definidas en el ámbito global solo se pueden usar en un determinado constructor, lo que hace que la función global no sea digna de su nombre.
Si el objeto necesita definir muchos métodos, no hay encapsulación alguna. Sin embargo,
estos se puede utilizar a través de prototipos para resolver

embalaje

La clase contenedora es el constructor.

// 原始值是没有属性的
var a = 123
a.len = 3
// new Number(123).len = 3 ;delete; 包装类的过程->赋值后但没有保存
console.log(a.len) // undefined

var obj = 3;
new Number(obj).name = '123' // 包装类的过程->执行完后没有保存
console.log(obj.name)

var str = 'abc'
console.log(str.length) // 3
console.log(new String(str).length) //包装类的过程,字符串本身有这个属性

// 数组的截断方法
var arr = [1,2,3,4,5]
arr.length = 3;
console.log(arr)

// 字符串没有截断方法

function Obj(){
    
    }
Obj.prototype.num = 1;
var obj1 = Object.create(Obj.prototype)
var obj2 = new Obj()
// obj1 , obj2是一样的

// 创建obj空对象
var obj = Object.create(null)
obj.num = 1; // 不是所有的对象都继承Object.prototype

var obj1 = {
    
    
    count:2
}

// 自己指定prototype是没有用的,我们只能修改它
obj.__proto__ = obj1;
console.log(obj,obj.count)

// undefined null 没有 toString 方法, 没有包装类
var num = 1;
num.toString()//包装类的原因

var num2 = new Number(num);
console.log(num2.toString());

var num = 1;
var obj = {
    
    };
var obj2 = Object.create(null);
document.write(num);
document.write(obj);
document.write(obj2); //报错,obj2 没有原型,也没有toString方法

Object.prototype.toString.call(1)

Patrón de prototipo

Comprender los objetos prototipo

// 1. 给原型对象添加单个属性
function Person(){
    
    
    // var this = {
    
    
    //     __proto__:Person.prototype
    // }
}
Person.prototype.name = 'Tom'
Person.prototype.sayNam = function(){
    
    
    alert(this.name)
}
var person1 = new Person()
console.log(person1)
// 打印出的对象有以下属性
// [[Prototype]]: Object
//      name: "Tom"
//      sayNam: ƒ ()
//      constructor: ƒ Person()
//      [[Prototype]]: Object
console.log(person1.__proto__)
//      name: "Tom"
//      sayNam: ƒ ()
//      constructor: ƒ Person()
//      [[Prototype]]: Object


// 2. 给原型对象添加多个属性,开发过程中经常这样写
function Person(){
    
    }
Person.prototype = {
    
    
    name:'Tom',
    sayNam:function(){
    
    
        alert(this.name)
    }
}
var person1 = new Person()
console.log(person1)

// =================

function Person(){
    
    }
Person.prototype.name = 'Tom'
Person.prototype.sayNam = function(){
    
    
    alert(this.name)
}
var person1 = new Person()
var person2 = new Person()
person1.sayName == person2.sayName //true

::: sugerencia para cada objeto instanciado

  1. Cada objeto instanciado tiene un atributo __proto__, que apunta al prototipo de la instancia del objeto actual.
  2. Sólo los objetos instanciados tienen prototipos.
  3. Los prototipos de objetos del mismo tipo y de diferentes instancias son los mismos.
  4. La ventaja de utilizar un objeto prototipo es que todas las instancias del objeto pueden compartir las propiedades y métodos contenidos en el prototipo.
    :::

::: consejo para cada constructor

  1. El atributo prototipo del constructor.
    • Cada constructor tiene un atributo de prototipo, que por defecto apunta al objeto prototipo del constructor.
    • El objeto prototipo tiene un constructor de atributos que apunta al constructor.
    • Después de crear un constructor personalizado, su objeto prototipo solo obtendrá la propiedad constructora del objeto función de forma predeterminada; en cuanto a otros métodos, se heredan de Object.
    • ES5 llama a este objeto prototipo [[Prototipo]], pero no tiene acceso estándar. El navegador admite un atributo __proto__ en cada objeto de instancia. Este vínculo existe entre la instancia y el objeto prototipo del constructor, no entre la instancia y el constructor.
  2. Agregar atributos (generalmente métodos) al objeto prototipo
    tiene el efecto: todos los objetos de instancia de la función tienen automáticamente los atributos (métodos) en el prototipo
    :::
console.log(Date.prototype)
console.log(typeof Date.prototype) //"object"
function fun(){
    
    }
console.log(fun.prototype)
/*
{
    constructor:fun(),
    [[Prototype]]:Object
}
*/

//给原型对象添加属性-->实例对象可以访问
fun.prototype.test=function(){
    
    console.log("test()")}
console.log(fun.prototype)
var fun1 = new fun();
fun1.test()

//原型对象有一个构造函数属性constructor,它指向函数对象
console.log(Date.prototype.constructor===Date) //true
console.log(fun.prototype.constructor===fun) //true
console.log(fun.prototype.constructor===fun1.constructor) //true

//函数与原型对象相互引用

:::consejo

  1. Agregue propiedades al objeto prototipo -> el objeto de instancia puede acceder
  2. El objeto prototipo tiene un constructor de atributos de constructor, que apunta al objeto de función.
  3. Las funciones y los objetos prototipo se refieren entre sí
    :::
    :::warning
    Aunque se puede acceder al valor almacenado en el prototipo a través de la instancia del objeto,
    el valor en el prototipo no se puede sobrescribir a través de la instancia del objeto
    . Si agregamos una propiedad al instancia y la propiedad Si tiene el mismo nombre que una propiedad en el prototipo de instancia,
    entonces creamos la propiedad en la instancia, que enmascarará el valor en el prototipo.
    Utilice el operador de eliminación para eliminar una propiedad con el mismo nombre creada en la instancia y acceder al valor en el prototipo nuevamente
    :::

cadena prototipo

:::consejo

  • El proceso de búsqueda de atributos a lo largo del prototipo del objeto formará una relación de herencia similar a una cadena, que se denomina cadena de prototipo.
  • La parte superior de la cadena de prototipos es Object.prototype en lugar de Object (el objeto también tiene un prototipo)
  • El prototipo del prototipo debe construirse a partir de Object
    :::
  • Pregunta de la entrevista 1
function Teacher(){
    
    
    this.students = 500;
}
var teacher = new Teacher();

function Student(){
    
    
}
Student.prototype = teacher;

var student = new Student();
student.students++
// student.students = student.students+1 
// 给student重新赋值了属性students
// 而不是修改student原型上的属性
console.log(student,teacher)
// Student {students: 501}  Teacher {students: 500}
// student.prototype.students = 100 对象实例没有prototype属性,只能通过__proto__访问原型

students.__proto__.students = 101 //students is not defined
console.log(students)
  • Pregunta de la entrevista 2

function Car(){
    
    
    this.brand = 'Benz';
}

Car.prototype = {
    
    
    brand:'Mazda',
    intro:function(){
    
    
        console.log('我是'+ this.brand +'车');
    }
}

var car = new Car();
// this 指向的是 car 本身 ,this:谁调用指向谁
car.intro(); //我是Benz车

// 以下两个相等
car.__proto__.intro()
Car.prototype.intro()
// VM882:8 我是Mazda车
  • Pregunta de la entrevista 3
function Person(){
    
    
    this.smoke = function(){
    
    
        this.weight--
    }
}

Person.prototype = {
    
    
    weight:130
}

var p = new Person();
p.smoke() //函数执行后返回undefined
// 原因。普通函数都有返回值,默认是undefined。构造函数的返回值是this
console.log(p)//Person {weight: 129, smoke: ƒ}
p.prototype.weight;//130

Prototipos explícitos y prototipos implícitos.

  1. Cada función tiene un atributo prototipo, que es un prototipo explícito.
  2. Cada instancia tiene un atributo __proto__, que es un prototipo implícito.
  3. El valor del prototipo implícito de un objeto es el valor del prototipo explícito de su constructor correspondiente.
  4. Resumir
    • El atributo prototipo de la función: se agrega automáticamente al definir la función, el valor predeterminado es un objeto vacío
    • El atributo __proto__ del objeto de instancia: se agrega automáticamente cuando se crea el objeto. El valor predeterminado es el atributo prototipo del constructor.
    • Los programadores pueden operar directamente prototipos explícitos, pero no pueden operar directamente prototipos implícitos (antes de es6)
//每个函数function都有一个prototype属性,即显式原型
function Fn(){
    
    } // 内部语句:this.prototype={}
console.log(Fn.prototype)

//每个实例都有一个__proto__属性,为隐式原型
var fn = new Fn() // 内部语句:this.__proto__ = Fn.prototype
console.log(fn.__proto__)

//对象的隐式原型的值为其对应构造函数的显示原型的值
console.log(Fn.prototype===fn.__proto__) //true

//给原型添加方法
Fn.prototype.test = function(){
    
    console.log('test()')}
//通过实例对象来调用原型上的方法
fn.test()

herencia de cadena prototipo

El objeto de instancia del constructor posee automáticamente las propiedades (métodos) del objeto prototipo de función
utilizando la cadena de prototipo.

  1. El objeto señalado por el prototipo explícito de la función por defecto es un objeto de instancia de objeto vacío (pero el objeto no lo satisface)
    console.log(Fn.prototype instanceof Object) //true
    console.log(Object.prototype instanceof Object)//false
    console.log(Function.prototype instanceof Object)//true
    
  2. La función es una instancia de sí misma.
    Todas las funciones son instancias de la función, incluida ella misma.
    console.log(Function.__proto__ === Function.prototype)//true
    
  3. El objeto prototipo de Object es el final de la cadena de prototipos.
    console.log(Object.prototype.__proto__)//null
    

Problema de atributos de la cadena de prototipos

function Fn(){
    
    }
Fn.prototype.a = 'xxx'
var fn1 = new Fn()
console.log(fn1.a)//'xxx'
var fn2 = new Fn()
fn2.a='yyy'
console.log(fn1.a,fn2.a)//'xxx' 'yyy'

function Person(name,age){
    
    
    this.name=name
    this.age=age
}
Person.prototype.setName=function(name){
    
    this.name=name}
var p1 = new Person('tom',12)
p1.setName('Bob')
console.log(p1)
  1. Al leer las propiedades de un objeto: buscará automáticamente en la cadena del prototipo
  2. Al configurar las propiedades de un objeto, no se buscará en la cadena de prototipos, si el objeto actual no tiene esta propiedad, agregue esta propiedad directamente y establezca su valor.
  3. Los métodos generalmente se definen en el prototipo y las propiedades generalmente se definen en el objeto mismo a través del constructor.

Explorar instancia de

function Foo(){
    
    }
var f1 = new Foo()
console.log(f1 instanceof Foo) //true
console.log(f1 instanceof Object) //true
console.log(f1 instanceof Function) //false

Cómo se juzga la instancia de
Expresión: Una instancia de B
Si el objeto prototipo explícito de la función B está en la cadena de prototipo del objeto A, devuelve verdadero; de lo contrario, devuelve falso

console.log(Object instanceof Function) //true
console.log(Object instanceof Object) //true
console.log(Function instanceof Object) //true
console.log(Function instanceof Function) //true

function Foo(){
    
    }
var foo = new Foo()
console.log(Object instanceof Foo) //false
console.log(foo instanceof Foo) //true
console.log(foo instanceof Function) //false
// foo is also an instance of Object because    
// Foo.prototype is an instance of Object.
// the interpreter will find the constructor  
// through the prototype chain.

// Prototype chain of the object foo   
// foo -> Foo.prototype -> Object.prototype -> null
// But foo is not an instance of Function, because
// we could not find Function.prototype in foo's 
// prototype chain.

¿Por qué tanto la instancia de objeto de función como la instancia de función de objeto devuelven verdadero?

Los objetos, funciones, matrices, etc. se denominan "funciones" constructoras y todas son funciones. Todas las funciones son instancias de la función constructora. Desde la perspectiva del mecanismo de cadena de prototipos, eso significa que todas las funciones pueden encontrar el objeto prototipo construido Function.protorype que creó su constructor de funciones a través de la cadena de prototipos, por lo que:js Object instanceof Function == true

Al mismo tiempo, debido a que Function.prototype es un objeto, su constructor es Object. Desde la perspectiva del mecanismo de la cadena de prototipos, eso significa que todas las funciones pueden encontrar el constructor de objetos que las creó a través de la cadena de prototipos. Construye el objeto prototipo. objeto prototipo, entonces:js Function instanceof Object==true

Lo interesante es que, según nuestro análisis de instancia de a través del mecanismo de cadena prototipo, podemos sacar fácilmente una conclusión: la función instancia de La función aún devuelve verdadero y el principio es el mismo.

para(var…en…)

:::consejo

  • Al atravesar el objeto, se imprimirán sus propias propiedades y las propiedades en la cadena del prototipo.

  • No se recomienda usarlo para atravesar matrices
    :::

  • Atravesar objetos

//函数执行完 把对象this返回
function Car(){
    
    
    this.brand = 'Benz'
    this.color = 'red'
    this.displacement='3.0'
}
Car.prototype = {
    
    
    lang:5,
    width:2.2
}
var car = new Car()
for(var key in car){
    
    
    console.log(key+':'+car[key])
}
/**
自己的属性和原型链上的属性都会被打印出来
brand:Benz
color:red
displacement:3.0
lang:5
width:2.2
**/
  • matriz transversal
var arr = [1,2,3,4]
for(var i in arr){
    
    
    console.log(i)
}

La matriz parece normal. Si agrega un método al prototipo de la matriz, también se imprimirán los valores en la cadena del prototipo.


Object.prototype.num = 1;
Array.prototype.test = 3

var arr = [1,2,3]

for(var i in arr){
    
    
    console.log(i,arr[i]);
}
// VM99:2 0 1
// VM99:2 1 2
// VM99:2 2 3
// VM99:2 test 3
// VM99:2 num 1
// undefined

tieneOwnProperty、en

:::consejo

  • hasOwnProperty se usa para verificar si la propiedad dada existe en la instancia del objeto actual (no en el prototipo de la instancia)
  • hasOwnProperty excluye prototipos, no excluye prototipos
  • Por lo tanto, siempre que el operador in devuelva verdadero y hasOwnProperty devuelva falso, puede estar seguro de que la propiedad es una propiedad en el prototipo
    :::
function Car(){
    
    
    this.brand = 'Benz'
    this.color = 'red'
    this.displacement='3.0'
}
Car.prototype = {
    
    
    lang:5,
    width:2.2
}
var car = new Car()
//hasOwnProperty 只打印自己的属性
for(var key in car){
    
    
    if(car.hasOwnProperty(key)){
    
    
        console.log(key+':'+car[key])
    }
}
  • hasOwnProperty excluye prototipos, no excluye prototipos
//-----------------

function Car(){
    
    
    this.brand = 'Benz'
    this.color = 'red'
}
Car.prototype = {
    
    
    displacement:'3.0'
}

// 没有通过构造函数,而是对象字面量
var car = {
    
    
    brand:'Benz',
    color:'red'
}
console.log('displacement' in car)//false

// 通过构造函数,
var car = new Car()
console.log('displacement' in car)//true

La diferencia entre bucles in y for(var…in…)

:::consejo

  • en se refiere a todas las propiedades en la cadena del prototipo
  • for(var…in…) se refiere a todas las propiedades personalizadas en la cadena del prototipo
    • Cuando se utiliza un bucle for-in, se devuelven todas las propiedades enumeradas a las que se puede acceder a través del objeto.
    • Esto incluye tanto las propiedades que existen en la instancia como las propiedades que existen en el prototipo.
    • Las propiedades de instancia que enmascaran propiedades no enumerables en el prototipo (es decir, propiedades que marcan [[Enumerable]] como falso) también se devolverán en el bucle for-in.

      Mi propia comprensión de este punto: la propiedad Object.toString no se puede enumerar. Si redefinimos la propiedad toString en la instancia del objeto, esta propiedad también se atravesará.

    • Porque por regulación, todas las propiedades definidas por el desarrollador son enumerables.
      :::
// ……
console.log('toString' in car)//true

for(var i in car){
    
    
    console.log(i,car[i]);
}
// VM234:2 brand Benz
// VM234:2 color red
// VM234:2 displacement 3.0
// VM234:2 num 1

Objeto.claves()

  • Para obtener todas las propiedades de instancia enumerables en una instancia de objeto, puede usar el método Object.keys() en ES5
function Person(){
    
    }
Person.prototype.name = 'Tom' 
Object.keys(Person.prototype)//["name"]

var p1 = new Person()
p1.aliasName = 'Job'
p1.age = 31
Object.keys(p1)// ["aliasName", "age"]

Objeto.getOwnPropertyNames()

  • Si desea obtener todas las propiedades de la instancia, ya sean enumerables o no, puede utilizar el método Object.getOwnPropertyNames().
Object.getOwnPropertyNames(p1)//["aliasName", "age"]
Object.getOwnPropertyNames(Person.prototype)//["constructor", "name"]
//注意结果中包含了不可枚举的constructor属性
  • Ambos métodos Object.keys y Object.getOwnPropertyNames se pueden utilizar para reemplazar el bucle for-in.

Sintaxis de prototipo más simple

function Person(){
    
    }
Person.prototype = {
    
    
    name:'Tom',
    sayName:function(){
    
    
        alert(this.name)
    }
}

En el código anterior, configuramos Person.prototype igual a un objeto creado como un objeto literal. Parece que el resultado final es el mismo, con una excepción:
la propiedad del constructor ya no apunta a Persona.
Como se mencionó anteriormente, cada vez que se crea una función, se creará su objeto prototipo al mismo tiempo y este objeto obtendrá automáticamente el atributo constructor.
La sintaxis que usamos aquí básicamente reescribe por completo el objeto prototipo predeterminado, por lo que la propiedad del constructor se convierte en la propiedad del constructor del nuevo objeto (que apunta al constructor del Objeto) y ya no apunta al constructor de la Persona.
En este momento, aunque instancia de todavía puede devolver el resultado correcto, el tipo de objeto no se puede determinar a través del constructor.

var f = new Person()
f instanceof Object //true
f instanceof Person //true
f.constructor == Person//false
f.constructor == Object //true

Si el valor del constructor es realmente importante, puedes establecer su valor específicamente

function Person(){
    
    }
Person.prototype = {
    
    
    constructor:Person,
    name:'Tom',
    sayName:function(){
    
    
        alert(this.name)
    }
}
f.constructor == Person // true

Tenga en cuenta que restablecer la propiedad del constructor de esta manera hará que su atributo [[Enumerable]] se establezca en verdadero.
De forma predeterminada, las propiedades del constructor nativo no son enumerables.
puedo usar

function Person(){
    
    }
Person.prototype = {
    
    
    name:'Tom',
    sayName:function(){
    
    
        alert(this.name)
    }
}

Object.defineProperty(Person.prototype,"constructor",{
    
    
    enumerable:false,
    value:Person
})

La dinámica de los prototipos.

Debido a que el proceso de encontrar un valor en el prototipo es una búsqueda, cualquier modificación realizada en el objeto prototipo se refleja inmediatamente en la instancia.
Incluso si primero crea una instancia y luego modifica el objeto prototipo

var f = new Person()
Person.prototype.sayHi = function(){
    
    
    alert('hi')
}
f.sayHi()

Pero la situación es diferente si se reescribe todo el objeto prototipo.
Rompe la conexión entre el constructor y el prototipo original.
El puntero del prototipo en la instancia apunta al prototipo en lugar del constructor.

function Person(){
    
    }
var f = new Person()
Person.prototype = {
    
    
    constructor:Person,
    sayHi:function(){
    
    
        alert('hi')
    }
}//把新的对象地址赋给了原型指针
f.sayHi()

En este ejemplo, primero se crea una instancia de Persona y luego se anula su objeto prototipo.
Luego se produjo un error cuando se llamó a f.sayHi()
porque el prototipo señalado por f no contenía una propiedad denominada con ese nombre.
Anular un objeto prototipo corta la conexión directa del prototipo existente con cualquier instancia de objeto previamente existente; todavía hacen referencia al prototipo original

Prototipo de objeto nativo.

A través del prototipo de un objeto nativo, no solo se pueden obtener referencias a todos los métodos predeterminados, sino que también se pueden definir nuevos métodos.

String.prototype.startsWith = function(text){
    
    
    return this.indexOf(text) == 0
};

var msg = 'Hello world'
alert(msg.startsWith('He'))//true 

No se recomienda modificar el prototipo de objetos nativos en aplicaciones de producción.

Problema con objetos nativos

El patrón prototipo no está exento de defectos: su mayor problema es causado por su naturaleza compartida.

function Person(){
    
    }
Person.prototype = {
    
    
    name:'Job',
    friends:['Tom','Shell'],
    sayHi:function(){
    
    
        alert('hi')
    }
}//把新的对象地址赋给了原型指针
var f1 = new Person()
var f2 = new Person()
f1.friends.push('test')
f1.friends == f2.friends //true

Hay una propiedad de amigos en Person.prototype, que es una matriz de cadenas.
Luego se crean dos instancias de Persona
y luego se modifica la matriz a la que hace referencia f1.friends. Debido a que esta matriz de atributos existe en el prototipo, f2.friends también se modifica.
Sin embargo, las instancias generalmente tienen sus propias propiedades únicas.
Este problema es el motivo por el que el patrón prototipo rara vez se utiliza solo.

Combinando patrón constructor y patrón prototipo

La forma más común de crear tipos personalizados es utilizar una combinación del patrón constructor y el patrón prototipo.
El patrón constructor se utiliza para definir propiedades de instancia.
El patrón prototipo se utiliza para definir métodos y propiedades compartidas.
Como resultado, cada instancia tendrá su propia copia de los atributos de la instancia, pero al mismo tiempo compartirá referencias de métodos, maximizando el ahorro de memoria.
Este modo híbrido también admite pasar parámetros al constructor.

function Person(name){
    
    
    this.name = name
    this.friends = ['Tom','Shell']

}
Person.prototype = {
    
    
    constructor:Person,
    sayHi:function(){
    
    
        alert('hi')
    }
}
var f1 = new Person()
var f2 = new Person()
f1.friends.push('test')
f1.friends == f2.friends //false
f1.sayHi == f2.sayHi

Este modo de fusión es el método más utilizado y reconocido para crear tipos personalizados.

Patrón de creación de prototipos dinámicos

Los desarrolladores con experiencia en otros lenguajes OO pueden sentirse muy confundidos al ver constructores y prototipos separados.
El patrón de prototipo dinámico aborda formalmente este problema encapsulando toda la información en el constructor.

function Person(name,age){
    
    
    //属性
    this.name = name;
    this.age = age;
    //方法
    //这里只有在sayName方法不存在的情况下才会将它添加到原型中
    //这段代码只会在初次调用构造函数时才会执行
    //此后,原型已经初始化结束。不需要再修改了
    //而且这里对原型的修改能够立即在所有实例中得到反映
    //所以这种方法可以说是非常完美了
    if(typeof this.sayName != 'function'){
    
    
        //还可以用instanceof方法检查类型
        Person.prototype.sayName = function(){
    
    
            alert(this.name)
        }
    }
}
var p = new Person('Tom',24)
p.sayName();

constructor parásito

La idea básica de este patrón es crear una función que simplemente encapsule el código que crea el objeto y luego devuelva el objeto recién creado. Esta función se parece mucho a un constructor típico.

function Person(name,age){
    
    
    var o = new Object()
    o.name = name
    o.age = age
    o.sayName = function(){
    
    
        alert(this.name)
    }
    return o
}
var p = new Person('Tom',19)
p.sayName()

En este ejemplo,
la función Persona crea un nuevo objeto, lo inicializa y devuelve el objeto.
Excepto por el uso del nuevo operador, este modo es exactamente el mismo que el modo de fábrica.
Hay que decir una cosa:
en primer lugar, no existe ninguna relación entre el objeto devuelto y el constructor o las propiedades prototipo del constructor.
Es decir, el objeto devuelto por el constructor no es diferente del objeto creado fuera del constructor.
Por este motivo, no puede confiar en el operador instancia de para determinar el tipo de objeto.
Se recomienda no utilizar este modo cuando se pueden utilizar otros modos.

Patrón de constructor de sonido

Referencia: https://blog.csdn.net/maomaolaoshi/article/details/73928094

Douglas Crockford inventó el concepto de objetos duraderos en JavaScript.

El llamado objeto seguro se refiere a un objeto que no tiene propiedades públicas y cuyos métodos no hacen referencia a esto.El
patrón de constructor seguro es similar al patrón de construcción parásito, pero hay dos diferencias.

  1. Los métodos de instancia de objetos recién creados no hacen referencia a esto
  2. No utilice el nuevo operador para llamar al constructor.
function Person(name,age,job){
    
    
    //创建要返回的对象
    var o=new Object();
    //可以在这里定义私有变量和函数
    //添加方法
    o.sayName=function(){
    
    
    alert(name);
    }
    //返回对象
    return o;
}
//调用
var friend=Person("Nicholas",29,"Software Engineer");
friend.sayName();//"Nicholas"
  1. ¿Cómo implementar variables y funciones privadas?

Antes de implementar variables y funciones privadas, primero comprendamos qué son las variables privadas.
variable privada

Cualquier variable definida dentro de una función puede considerarse privada porque no se puede acceder a ella fuera de la función. Las variables privadas incluyen parámetros de función, variables locales y otras funciones definidas dentro de la función.

function Person(name,age,job){
    
    
//定义私有(局部)变量
    var name=name,
        age=age,
        job=job;
}
var person1= Person("猫猫老师",18,"前端");
    console.log(person1.name)//Cannot read property 'name' of undefined

El nombre, la edad y el trabajo son variables privadas de esta función. En el estado actual, no tiene forma de acceder a estas variables.
Ahora que sabemos esto, echemos un vistazo a cómo implementar funciones y variables privadas.

    function Person(name,age,job){
    
    
    //创建要返回的对象
    var o=new Object();
    //定义私有(局部)变量
    var name=name,
        age=age,
        job=job;
    //添加方法
    o.sayName=function(){
    
    
        alert(name);
    }
    //返回对象
    return o;   
}
  1. ¿Qué pasaría si pusiéramos la variable privada en el objeto o?
    function Person(name,age,job){
    
    
    //创建要返回的对象
    var o=new Object();
    //把私有变量放到对象o上
        o.name=name,
        o.age=age,
        o.job=job;
    //添加方法
    o.sayName=function(){
    
    
        alert(name);
    }
    //返回对象
        return o;
}

¡Esto puede provocar problemas de seguridad muy graves! Porque podemos alterar u obtener las variables de la función modificando o agregando el método del objeto o!

    var person1 = Person("猫猫老师", 18, "前端");
    //为函数添加方法
    person1.sayAge = function() {
    
    
        console.log(person1.age);
    }
    person1.sayAge(); //18
    //篡改函数的变量
    person1.name = "喵";
    console.log(person1.name);//喵
    //篡改函数的方法
    person1.sayName = function() {
    
    
        console.log(person1.job);
    }
    person1.sayName(); //前端
  1. ¿Por qué o.sayName puede acceder a las variables privadas de la función?

Antes de responder a esta pregunta, primero debemos comprender un concepto: el método de privilegios.

método privilegiado

Llamamos métodos privilegiados a los métodos públicos que tienen acceso a variables privadas y funciones privadas.

function Person(name,age,job){
    
    
    //定义私有变量
    var name=name,
        age=age,
        job=job,
        //定义特权方法
        publicMethod=function(){
    
    
        return "姓名:"+name+
                " 年龄:"+age+
                " 工作:"+job
    }
    return  publicMethod;   
}
var person1= Person("猫猫老师",18,"前端");
console.log(person1());//姓名:猫猫老师 年龄:18 工作:前端

Al llamar a un método privilegiado, obtenemos las variables privadas de la función.

Principio de implementación de métodos privilegiados:
los métodos privilegiados aquí en realidad utilizan el principio de cierre: las funciones internas se pueden usar para acceder a variables de funciones externas.

Para decirlo de manera más general, las variables locales pueden acceder a variables globales.

var o="全局变量";
function foo(){
    
    
    var o2=o;
    console.log(o2);
}
foo();//全局变量

Para las funciones internas, las funciones externas son equivalentes al entorno global y pueden acceder naturalmente a sus variables y funciones.
o.sayName es una función interna para Persona, por lo que puede acceder naturalmente a las variables privadas de Persona.

  1. ¿Puedo agregar un método fuera de la función para llamar a la variable privada de la función?

Respuesta: No.

function Person(name,age,job){
    
    
//定义私有变量
var name1=name,
    age=age,
    job=job;    
}
Person.sayAag=function(){
    
    
    console.log(age);
}
Person.prototype.sayJob=function(){
    
    
    console.log(job);
}       
var person1= Person("猫猫老师",18,"前端");
person1.sayAag()//Cannot set property 'sayAag' of undefined
person1.sayJob()// Cannot read property 'sayJob' of undefined

De hecho, el método de extender métodos para funciones a través de "nombre de función.método" se denomina variable estática.

function Person(name, age, job) {
    
    };
//定义静态属性
Person.staticVar = "静态属性";
console.log(Person.staticVar);//静态属性
//定义静态方法
Person.staticFun=function(){
    
    console.log("静态方法");}
Person.staticFun();//静态方法

¡Las variables definidas a través de "nombre de función.método" solo se pueden llamar a través de nombre de función.nombre de método! Estas variables no son accesibles a través de la instancia.

function Person(name, age, job) {
    
    };
Person.staticVar = "静态属性";
console.log(new Person().staticVar);//undefined
  1. ¿Qué significa el patrón constructor de sonido?

En primer lugar, a partir de las cuatro preguntas anteriores, podemos obtener un resumen del patrón de constructor seguro:
Las características del patrón de constructor seguro:

  • La función devuelve un objeto a través del cual podemos acceder o modificar datos internos.
  • No puede acceder ni modificar los datos internos de la función agregando métodos a la función.
    Significado:
    en los objetos creados en este modo, no hay otra forma de acceder al valor del nombre excepto usar el método sayName().
    Incluso si otro código agrega métodos o miembros de datos a este objeto, no hay otra forma de acceder a los datos originales pasados ​​al constructor.
    La seguridad proporcionada por el patrón constructor de sonido lo hace muy adecuado para su uso en entornos proporcionados por ciertos entornos de ejecución seguros.

Supongo que te gusta

Origin blog.csdn.net/uotail/article/details/124717089
Recomendado
Clasificación