JavaScript understands objects

One hallmark of object-oriented languages ​​is that they all have the concept of classes.
Through classes, you can create any number of objects with the same properties and methods.
An object in JavaScript is defined as "an unordered collection of properties whose property values ​​can include basic values, objects, or functions."

Create object

Object constructor pattern

  • First create an empty object object, and then dynamically add properties and methods
  • Usage scenario: starting data with uncertain object content

The simplest way to create a custom object is to create an instance of Object and then add properties and methods to it.

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

Early developers often used this pattern to create new objects.

object literal pattern

:::tip
Now object literals have become the preferred mode for creating objects.
In the object, this is the object itself
:::

  • Use {} to create an object and specify attribute methods
  • Usage scenario: Data that initially determines the content of an object
  • Problem: If you create multiple objects the code is very repetitive
var person = {
    
    
    name:'Tom',
    age:29,
    sayName:function(){
    
    
        alert(this.name)
    }
}

Access to object properties

obj.prop

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

person.name

obj[‘prop’]

  1. When the attribute name contains special characters, it needs to be accessed with square brackets
    var p={
          
          }
    //p.content-type='text/json'
    p['content-type']='text/json'
    
  2. Variable name is uncertain
    var propName = 'myAge'
    var value = 18
    //p.propName=value
    p[propName]=value
    

Deletion of object attributes

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

delete person.name
delete person.sayName

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

Property type

ECMAScript has two types of attributes: data attributes and accessor attributes.

data attributes

Data attributes have 4 characteristics that describe their behavior

  • [[Configurable]]: Can the attribute be deleted and redefined by delete? Is it configurable?
  • [[Enumerable]]: Can attributes be returned through a for-in loop?
  • [[Writable]]: Can the value of the attribute be modified?
  • [[Value]]: the data value of the attribute

For properties defined directly on the object as in the previous example, their [[Configurable]], [[Enumerable]], [[Writable]] are all true, and [[Value]] is set to the specified value (if If not specified, it is undefined)

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

To modify the default properties of a property, you must use the Object.defineProperty() method.
This method receives three parameters: the object where the property is located, the name of the property and a descriptor object.
The attributes of the descriptor object must be: configurable, enumerable, writable and value

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

accessor properties

The accessor property does not contain a data value, it is a pair of getter and setter functions (although these two functions are not required).
Accessor properties have the following 4 characteristics:

  • [[Configurable]]: Can the attribute be deleted and redefined by delete? Is it configurable?
  • [[Enumerable]]: Can attributes be returned through a for-in loop?
  • [[Get]]: function called when reading properties defaults to undefined
  • [[Set]]: function called when writing a property default value undefined

Accessor properties cannot be defined directly and must be defined through 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 is an ES5 method. Before this method, to create accessor properties, two non-standard methods, defineGetter and defineSetter , are generally used.

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

Define multiple properties

ES5 also defines an Object.defineProperties() method.
Use this method to define multiple properties at once through descriptors.

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)
*/

Read attribute characteristics

Use the ES5 Object.getOwnPropertyDescriptor() method to obtain the descriptor of a given property.
This method receives two parameters, the object where the property is located and the name of the property from which its descriptor should be read.
The return value is an object, which can be an accessor property collection object or a data property collection object.

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}

Optimization of created objects

Although the Object constructor or object literal can be used to create a single object,
these methods have an obvious disadvantage: creating many objects using the same interface will produce a lot of duplicate code.
To solve this problem, people began to use some variations of the factory pattern

Factory pattern

Since classes cannot be created in ES, developers invented a function to encapsulate instances of objects created with a specific interface.

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')

:::warning

  1. Although the factory pattern solves the problem of creating multiple familiar objects,
  2. But it does not solve the problem of object recognition (that is, how to know the type of an object, the objects it creates do not have a specific type, they are all object types)
  3. So a new mode of creating objects emerged
    :::

constructor pattern

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

:::tip

  • Some special features of the constructor can be found:
    • Objects are created explicitly using new
    • Directly assign properties and methods to this
    • no return statement
    • Function names are capitalized (by convention, constructors always start with a capital letter, and non-constructor functions start with lowercase. This approach is borrowed from other OO languages).
      :::

Best practices for constructor parameter passing

//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('李四','女')
  • Best Practice: Passing Object Parameters When Using Constructors
//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>

new(this) operator-constructor instantiation principle

:::tip new function

  1. Create a new object
  2. Assign the scope of the constructor to the new object (so this points to the new object)
  3. Execute code in constructor
  4. Return new object
    :::
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,转换为指向为实例化的对象

:::warning

  1. If the return value is a non-reference value, it will not recognize it and still implicitly return this
  2. If a reference value is returned, the reference value is returned correctly
    :::
function Car(color,brand){
    
    
    this.color=color
    this.brand = brand
    return '123'
    //如果return的是非引用值,它不会识别 仍然隐式返回 this
    //如果return 引用值 ,则正确返回
}
var car = new Car('red','Mazda')//123

Constructor attributes constructor and instanceof

Instances of objects of the same type have a constructor attribute, which points to the constructor

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

The constructor property of an object is initially used to identify the object type.
But for detecting object types, the instanceof operator is more reliable.
The objects created above are all instances of Object and
are also instances of Person.

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

Creating a custom constructor means that future instances of it can be identified as a specific type.
And this is where the constructor beats the factory pattern

Treat constructors as functions

The only difference between a constructor and other functions is the way it is called.
As long as any function is called through the new operator, it can be used as a constructor.
If any function is not called through the new operator, then it is no different from an ordinary function.

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

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

Constructor problem

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

Looking at the constructor from this perspective, we find that the Function instance of each constructor instance is different.
Therefore, functions with the same name on different instances are not equal
, but functions with the same name on each instance perform the same function, so there is no need to create more.

person1.sayName == person2.sayName //falses

Solve this problem by moving the function definition outside the constructor

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

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

This can indeed solve the problem of re-creating the object instance's function with the same name every time.
But a new problem comes again:
functions defined in the global scope can only be used in a certain constructor, making the global function unworthy of its name.
If the object needs to define many methods, there is no encapsulation at all. However
, these can be used through prototypes. to solve

Packaging

The wrapper class is the 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)

Prototype pattern

Understanding prototype objects

// 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

::: tip for each instantiated object

  1. Each instantiated object has a __proto__ attribute, which points to the prototype of the current object instance.
  2. Only instantiated objects have prototypes
  3. The prototypes of objects of the same type and different instances are the same.
  4. The advantage of using a prototype object is that all object instances can share the properties and methods contained in the prototype.
    :::

::: tip for each constructor

  1. The prototype attribute of the constructor
    • Each constructor has a prototype attribute, which by default points to the prototype object of the constructor
    • The prototype object has an attribute constructor that points to the constructor
    • After creating a custom constructor, its prototype object will only obtain the constructor property of the function object itself by default; as for other methods, they are inherited from Object
    • ES5 calls this prototype object [[Prototype]], but it has no standard access. The browser supports an attribute __proto__ on each instance object. This link exists between the instance and the prototype object of the constructor, not between the instance and the constructor
  2. Adding attributes (usually methods) to the prototype object
    has the effect: all instance objects of the function automatically have the attributes (methods) in the prototype
    :::
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

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

:::tip

  1. Add properties to the prototype object –> the instance object can access
  2. The prototype object has a constructor attribute constructor, which points to the function object
  3. Functions and prototype objects refer to each other
    :::
    :::warning
    Although the value stored in the prototype can be accessed through the object instance,
    the value in the prototype cannot be overwritten through the object instance
    . If we add a property to the instance, and the property If it has the same name as a property in the instance prototype,
    then we create the property in the instance, which will mask the value in the prototype.
    Use the delete operator to delete a property with the same name created in the instance and access the value in the prototype again
    :::

prototype chain

:::tip

  • The process of searching for attributes along the object's prototype will form a chain-like inheritance relationship. This chain is called the prototype chain.
  • The top of the prototype chain is Object.prototype instead of Object (Object also has a prototype)
  • The prototype of the prototype must be constructed from Object
    :::
  • Interview question 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)
  • Interview question 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车
  • Interview question 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

Explicit prototypes and implicit prototypes

  1. Every function has a prototype attribute, which is an explicit prototype
  2. Each instance has a __proto__ attribute, which is an implicit prototype
  3. The value of an object's implicit prototype is the value of its corresponding constructor's explicit prototype.
  4. Summarize
    • The prototype attribute of the function: automatically added when defining the function, the default value is an empty object
    • The __proto__ attribute of the instance object: automatically added when the object is created. The default value is the prototype attribute of the constructor.
    • Programmers can directly operate explicit prototypes, but cannot directly operate implicit prototypes (before 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()

prototype chain inheritance

The instance object of the constructor automatically possesses the properties (methods) of the function prototype object
using the prototype chain.

  1. The object pointed to by the explicit prototype of the function defaults to an empty object instance object (but object does not satisfy it)
    console.log(Fn.prototype instanceof Object) //true
    console.log(Object.prototype instanceof Object)//false
    console.log(Function.prototype instanceof Object)//true
    
  2. Function is an instance of itself.
    All functions are instances of Function, including itself.
    console.log(Function.__proto__ === Function.prototype)//true
    
  3. The prototype object of Object is the end of the prototype chain
    console.log(Object.prototype.__proto__)//null
    

Prototype chain attribute problem

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. When reading the properties of an object: it will automatically search in the prototype chain
  2. When setting the properties of an object, the prototype chain will not be searched. If the current object does not have this property, add this property directly and set its value.
  3. Methods are generally defined in the prototype, and properties are generally defined on the object itself through the constructor.

Explore instanceof

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

How instanceof is judged
Expression: A instanceof B
If the explicit prototype object of function B is on the prototype chain of object A, return true, otherwise return false

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.

Why do both Object instanceof Function and Function instanceof Object return true?

Object, Function, Array, etc. are all called constructor "functions", and they are all functions. All functions are instances of the constructor Function. From the perspective of the prototype chain mechanism, that means that all functions can find the constructed prototype Function.protorype object that created their Function constructor through the prototype chain, so:js Object instanceof Function == true

At the same time, because Function.prototype is an object, its constructor is Object. From the perspective of the prototype chain mechanism, that means that all functions can find the Object constructor that created them through the prototype chain. Constructs the prototype Object.prototype object, so:js Function instanceof Object==true

What’s interesting is that based on our analysis of instanceof through the prototype chain mechanism, we can easily draw a conclusion: Function instanceof Function still returns true, and the principle is the same

for(var…in…)

:::tip

  • When traversing the object, its own properties and properties on the prototype chain will be printed.

  • It is not recommended to use it to traverse arrays
    :::

  • Traverse objects

//函数执行完 把对象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
**/
  • Traverse array
var arr = [1,2,3,4]
for(var i in arr){
    
    
    console.log(i)
}

The array seems normal. If you add a method to the array prototype, the values ​​on the prototype chain will also be printed.


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

hasOwnProperty、in

:::tip

  • hasOwnProperty is used to check whether the given property exists in the instance of the current object (not in the prototype of the instance)
  • hasOwnProperty excludes the prototype, but in does not exclude the prototype
  • Therefore, as long as the in operator returns true and hasOwnProperty returns false, you can be sure that the property is a property in the prototype
    :::
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 excludes prototypes, in does not exclude prototypes
//-----------------

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

The difference between in and for(var…in…) loops

:::tip

  • in refers to all properties on the prototype chain
  • for(var…in…) refers to all custom properties on the prototype chain
    • When using a for-in loop, all enumerated properties that can be accessed through the object are returned.
    • This includes both properties that exist in the instance and properties that exist in the prototype.
    • Instance properties that mask non-enumerable properties in the prototype (that is, properties that mark [[Enumerable]] as false) will also be returned in the for-in loop.

      My own understanding of this point: The Object.toString property cannot be enumerated. If we redefine the toString property in the object instance, this property will also be traversed.

    • Because by regulation, all developer-defined properties are enumerable.
      :::
// ……
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

Object.keys()

  • To obtain all enumerable instance properties on an object instance, you can use the Object.keys() method in 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"]

Object.getOwnPropertyNames()

  • If you want to get all instance properties, whether enumerable or not, you can use the Object.getOwnPropertyNames() method
Object.getOwnPropertyNames(p1)//["aliasName", "age"]
Object.getOwnPropertyNames(Person.prototype)//["constructor", "name"]
//注意结果中包含了不可枚举的constructor属性
  • Both methods Object.keys and Object.getOwnPropertyNames can be used to replace the for-in loop.

Simpler prototype syntax

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

In the code above, we set Person.prototype equal to an object created as an object literal. It looks like the end result is the same, with one exception:
the constructor property no longer points to Person.
As mentioned before, every time a function is created, its prototype object will be created at the same time, and this object will automatically obtain the constructor attribute.
The syntax we use here essentially completely rewrites the default prototype object, so the constructor property becomes the constructor property of the new object (pointing to the Object constructor) and no longer points to the Person constructor.
At this time, although instanceof can still return the correct result, the type of object cannot be determined through the constructor.

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

If the value of constructor is really important, you can set its value specifically

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

Note that resetting the constructor property in this way will cause its [[Enumerable]] attribute to be set to true.
By default, native constructor properties are not enumerable.
can use

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

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

The dynamics of prototypes

Because the process of finding a value in the prototype is a search, any modifications made to the prototype object are immediately reflected on the instance.
Even if you create an instance first and then modify the prototype object

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

But the situation is different if the entire prototype object is rewritten.
It breaks the connection between the constructor and the original prototype.
The prototype pointer in the instance points to the prototype rather than the constructor

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

In this example, an instance of Person is first created and then its prototype object is overridden.
Then an error occurred when f.sayHi() was called
because the prototype pointed to by f did not contain a property named by that name.
Overriding a prototype object severs the existing prototype's direct connection to any previously existing object instances; they still refer to the original prototype

Prototype of native object

Through the prototype of a native object, not only can you obtain references to all default methods, but you can also define new methods.

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

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

It is not recommended to modify the prototype of native objects in production applications.

Problem with native objects

The prototype pattern is not without its shortcomings. Its biggest problem is caused by its shared nature.

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

There is a friends property in Person.prototype, which is a string array.
Then two instances of Person are created,
and then the array referenced by f1.friends is modified. Because this attribute array exists in the prototype, f2.friends is also modified.
However, instances generally have their own unique properties.
This problem is why the prototype pattern is rarely used alone.

Combining Constructor Pattern and Prototype Pattern

The most common way to create custom types is to use a combination of the constructor pattern and the prototype pattern.
Constructor pattern is used to define instance properties.
The prototype pattern is used to define methods and shared properties.
As a result, each instance will have its own copy of the instance attributes, but at the same time share method references, maximizing memory savings.
This hybrid mode also supports passing parameters to the 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

This blending mode is the most widely used and recognized method of creating custom types.

Dynamic prototyping pattern

Developers experienced in other OO languages ​​may be very confused when seeing separate constructors and prototypes.
The dynamic prototype pattern formally addresses this problem by encapsulating all information in the 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();

parasitic constructor

The basic idea of ​​this pattern is to create a function that simply encapsulates the code that creates the object and then returns the newly created object. This function looks a lot like a typical constructor.

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()

In this example
the Person function creates a new object, initializes it and returns the object.
Except for using the new operator, this mode is actually exactly the same as the factory mode.
One thing needs to be said:
First of all, there is no relationship between the returned object and the constructor or the prototype properties of the constructor.
That is to say, the object returned by the constructor is no different from the object created outside the constructor.
For this reason, you cannot rely on the instanceof operator to determine the object type.
It is recommended not to use this mode when other modes can be used

Sound Constructor Pattern

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

Douglas Crockford invented the concept of durable objects in JavaScript.

The so-called safe object refers to an object that has no public properties and whose methods do not reference this. The
safe constructor pattern is similar to the parasitic construction pattern, but there are two differences.

  1. Instance methods of newly created objects do not reference this
  2. Do not use the new operator to call the 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. How to implement private variables and functions?

Before implementing private variables and functions, let's first understand what private variables are.
private variable

Any variables defined within a function can be considered private because they cannot be accessed outside the function. Private variables include function parameters, local variables, and other functions defined within the function.

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

Name, age, and job are all private variables of this function. In the current state, you have no way to access these variables.
Now that we know this, let's take a look at how to implement private variables and functions.

    function Person(name,age,job){
    
    
    //创建要返回的对象
    var o=new Object();
    //定义私有(局部)变量
    var name=name,
        age=age,
        job=job;
    //添加方法
    o.sayName=function(){
    
    
        alert(name);
    }
    //返回对象
    return o;   
}
  1. What would happen if we put the private variable on object 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;
}

This can lead to very serious security issues! Because we can tamper with or obtain the variables of the function by modifying or adding the method of object 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. Why can o.sayName access the private variables of the function?

Before answering this question, we must first understand a concept - the privilege method.

privileged method

We call public methods that have access to private variables and private functions privileged methods.

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 工作:前端

By calling a privileged method, we obtain the function's private variables.

Implementation principle of privileged methods:
The privileged methods here actually utilize the principle of closure - internal functions can be used to access external function variables.

To put it more generally, local variables can access global variables.

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

For internal functions, external functions are equivalent to the global environment and can naturally access its variables and functions.
o.sayName is an internal function for Person, so you can naturally access Person's private variables.

  1. Can I add a method outside the function to call the private variable of the function?

Answer: 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

In fact, the method of extending methods for functions through "function name.method" is called a static variable.

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

Variables defined through "function name.method" can only be called through function name.method name! These variables are not accessible through the instance.

function Person(name, age, job) {
    
    };
Person.staticVar = "静态属性";
console.log(new Person().staticVar);//undefined
  1. What does the sound constructor pattern mean?

First of all, from the above four questions, we can have a summary of the safe constructor pattern:
The characteristics of the safe constructor pattern:

  • The function returns an object through which we can access or modify internal data.
  • You cannot access or modify the function's internal data by adding methods to the function.
    Meaning:
    In objects created in this mode, there is no other way to access the value of name except using the sayName() method.
    Even if other code adds methods or data members to this object, there is no other way to access the original data passed into the constructor.
    The safety provided by the sound constructor pattern makes it well suited for use in environments provided by certain secure execution environments.

Guess you like

Origin blog.csdn.net/uotail/article/details/124717089