“JavaScript Advanced Programming (3rd edition)” study notes

Related content:

Learning JavaScript (3)- Talk about prototype chains – 1. Variables

Learning JavaScript (3)- Talk about prototype chains – 2. Objects and prototypes

Learning JavaScript (3)- Talk about prototype chains – 3. Prototype chains and inheritance


Key words: object prototype attribute; Instance attribute; How objects are created: prototype mode, constructor mode, parasitic mode

Learning route of this chapter:

  1. What is an object? Object properties?

  2. Modes for creating objects: Factory mode, constructor mode, prototype mode, dynamic prototype mode, Parasitic constructor mode, secure constructor mode

  3. In-depth understanding of prototype patterns:

  • Benefits and problems of prototyping patterns – Shared
  • Solution: use of constructors and stereotype patterns – constructors store instance properties, stereotype objects store shared properties

1. Understanding of objects

// Create an object
var person = {
  name: 'kenny'.age: 29.sayName: function () {
    alert(this.name) // create an Object directly}}Copy the code

1.1 Attribute Types

1.1.1 Data Attributes

Data attribute: A position containing a data value. This location can be read and written

Attribute values define The default value
[[Configurable]] Properties that can be modified, once changed to false, cannot be restored true
[[Enumerable]] Whether a for-in loop can be used to return properties true
[[Writable]] Whether the value of an attribute can be modified true
[[Value]] The data value of this property undefined

Attributes can be modified with object.defineproperty () :(use caution)

var person = {}
Object.defineProperty(person, 'name', {
  writable: false.value: 'Teller'
})
Copy the code

1.1.2 Accessor Properties

Accessor properties: Do not contain data values and contain getters and setters

Attribute values define The default value
[[Configurable]] Properties that can be modified, once changed to false, cannot be restored true
[[Enumerable]] Whether a for-in loop can be used to return properties True
[[Get]] Called when a property is read undefined
[[Set]] Called when a property is written undefined

1.2 Defining Multiple Attributes –Object.defineProperties()

var book = {}

Object.defineProperties(book, {
  _year: {
    writable: true.value: 2004
  },
  
  edition: {
    writable: true.value: 1
  },
  
  year: {
    get: function() {
      return this._year
    }
  }
})
Copy the code

1.3 Reading Attributes –Object.getOwnPropertyDescriptior()

This method can be used by any DOM and BOM object to obtain object properties

var descriptor = Object.getOwnPropertyDescriptior(book,  '_year')

alert(descriptor.value) / / 2004
alert(descriptor.configurable) // false
Copy the code

2. How to create an object

Keywords: factory pattern, constructor, prototype pattern, combined use, dynamic prototype, safe constructor, parasitic constructor

2.1 Factory Mode

  • Factory pattern: Encapsulate a function, construct the entire object, and return the object at the end
  • Problem: You can’t use the instanceof operator to determine the type of the object (because the method is not a constructor pattern) – use constructors to solve the problem
function createPerson (name, age, job) {
  // 1. Create an object
  var o = new Object(a)// 2. Add attributes to the object
  o.name = name
  o.age = age
  o.job = job
  o.sayName = function () {
    alert(this.name)
  }
  
  // 3. You need to return the object at the end
  return o 
}

// Call the function to construct an instance - without using the new operator
var person1 = createPerson('Greg'.27.'Doctor')

// Problem - Instanceof cannot be used to determine if Person1 is an object of type createPerson
alert(person1 instanceof createPerson) // false
alert(person1 instanceof Object)       // true
Copy the code

2.2 Constructor pattern

  1. Constructor: Assigns properties and methods directly to this without returning

  2. Differences from factory mode:

    1. There is no explicit creation object
    2. Attributes and methods are given directly to this object
    3. No return statement
  3. Factory pattern problem solved: objects created using constructors can be instanceof the type of the object

  4. Disadvantages of constructors: properties that are functionally identical cannot be shared and take up memory space repeatedly – solve this problem with the prototype pattern

// constructor Person (usually uppercase)
function Person (name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.sayName = function() {
    alert(this.name)
  }
}

// Call the constructor:
// 1. Use the new method to create the person object using the constructor
var person1 = new Person('Greg'.29.'Doctor')
alert(person1 instanceof Person) // true - You can use instanceof to get the object type

// 2. When used as a normal function, create an object for the window - this in Person() points to the window
Person('Greg'.29.'Doctor')
window.sayName() // 'Greg'


// The problem is that the constructor creates attributes that are instance attributes. Properties that have exactly the same effect cannot be shared and occupy memory space repeatedly
alert(person1.sayName == person2.sayName) // false

/* * For example: The sayName() of two objects constructed by Person is not equivalent, but both perform the same function. * Common solution: Remove the shared property from the constructor as a global property. If an object needs multiple functions, declare multiple functions globally, violating encapsulation * the best solution: the prototype pattern */
function Person (name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.sayName = sayName
}
function sayName() { // Global function - lacks encapsulation
  alert(this.name)
}
Copy the code

2.3 Prototype mode – Prototype

2.3.1 Creation method and basic concept of prototype pattern

  1. Prototype pattern: Each function or Object created has a prototype property
  2. The Prototype property is a pointer to an object containing a property core method shared by all instances of a particular type
  3. Advantages: Allows all object instances to share their contained properties and methods without having to define object instance information in constructors
  4. Note: Properties with the same name created in the instance override properties in the stereotype
function Person() {} // constructor Person
  
// Add attributes in the prototype of Person instead of in the constructor
Person.prototype.name = 'Nicholas'
Person.prototype.name = 29
Person.prototype.sayName = function () { alert(this.name) }
  
// Create object - Use the Person constructor to create a Person object that can directly use the stereotype properties in Person
var person1 = new Person()
person1.sayName() // 'Nicholas'
  
var person2 = new Person()
person2.sayName() // 'Nicholas'
  
alert(person1.sayName == person2.sayName) // true - Create different objects that point to the same stereotype property (same pointer)
Copy the code
  1. Another way to create prototypes: object literals
// Object literals
Person.prototype = {
  name: 'Nicholas'.age: 29.job: 'Doctor'.sayName: function() {
    alert(this.name)
  }
}
Copy the code

Object literals have problems: the Constructor property of the Prototype object no longer points to Person

Reason: The default Prototype Object is completely overwritten by the above method, so the constructor property becomes the constructor property of the new Object, pointing to the Object constructor instead of the Person function

var person1 = new Person()
alert(person1 instanceof Object) // true
alert(person1 instanceof Person) // true
alert(person1.constructor == Person ) // The constructor of the false - person1 Object refers directly to Object because it overrides the prototype Object
alert(person1.constructor == Object) // true

// Return constructor from the overridden object as well
Person.prototype = {
  constructor: Person, In the overwritten prototype object, set Person to the constructor value
  name: 'Nicholas'.age: 29.job: 'Doctor'.sayName: function() {
    alert(this.name)
  }
}
// However, the above method causes Constructor to become enumerable. In fact, constructor is not enumerable by default, so you need to set it using the defineProperty method
Object.defineProperty(Person.prototype, 'constructor', {
  enumerable: false.// Reset to false
  value: Person
}) 
Copy the code

2.3.2 Understanding of prototype objects – Shared attributes

  1. Example code:

The illustration is as follows:

Person Prototype: constructor () {name, age, sayName; The constructor in the prototype object refers back to the Person constructor. 3. Person1 and Person2 objects created using the Person constructor: Person1 and Person2 refer to the sayName attribute in the same Prototype objectCopy the code

A. Prototype object parsing (according to the above summary) :

When you create a new function, you create a prototype property for the function that points to the function's prototype object. All prototype objects automatically get a constructor attribute, which is a pointer to the function where the Prototype attribute resides. When you create an object from the constructor, it contains a pointer to the constructor's prototype objectCopy the code

B. YesisPrototype()andObject.getPrototypeOf()Method to determine whether an object points to a constructor (because it is not accessible in a regular implementation) :

// 1. Use the isPrototype() method to determine the prototype relation between the object and the constructor
Person.prototype.isPrototypeOf(person1) // true

// 2. Use es5 object.getProtoTypeof () to return [[Prototype]]
Object.getPrototypeOf(person1).name // 'Nicholas'
Object.getPrototypeOf(person1) == Person.prototype // true
Copy the code

C. You cannot override a value in the prototype by creating an object instance with the same name

// 1. Create the constructor Person
function Person() {}

// 2. Create a prototype object for Person
Person.prototype.name = 'Nicholas'
Person.prototype.age = 29
Person.prototype.job = 'Doctor'
Person.prototype.sayName = function() { alert(this.name) }

// 3. Create the Person object person1
var person1 = new Person()

// 4. Create the name attribute value from the constructor instance
person1.name = 'Greg'

// 5. The name attribute in the instance overrides the name attribute in prototype
alert(person1.name) // 'Greg'

// 6. Run delete to delete the attribute value in the instance
delete person1.name

// 7. Attribute values in the prototype are displayed when called because attributes in the instance are deleted
alert(person1.name) // 'Nicholas'
Copy the code

2.3.3 Method to determine whether the attribute is in the prototype or in the instance

A. Determine whether the attribute comes from an instance:hasOwnProperty()– Returns true if from an instance

var person1 = new Person()

// The 1. name attribute comes from the stereotype, so return false
alert(person1.hasOwnProperty('name')) // false

// 2. Create an attribute in the instance and return true
person1.name = 'Greg'
alert(person1.hasOwnProperty('name')) // true
Copy the code

B. Determine whether the attribute comes from an instance or object:hasOwnProperty(), use the in operator alone

  1. inOperator: can be used alone or infor-inLoop to determine if the property exists when used aloneExample or prototypeIn the
// Use the in method to verify that the name attribute exists in the instance or stereotype
var person1 = new Person()
alert('name' in person1) // The true -- name attribute exists in the instance or stereotype
Copy the code
  1. Object.hasOwnProperty()Is the Object method, which can be used to determine whether a property exists inThe instanceIn the
// Verify that the property exists in the instance with the object.hasownProperty () method
var person1 = new Person()
alert(person1.hasOwnProperty('name')) // The false -- name attribute does not exist in the instance
Copy the code
  1. In addition, the above two methods can be customized wrapped to determine whether the attribute exists in the stereotype
Using object.hasownProperty () and the in operator, encapsulate a custom method to determine whether the property exists in the prototype
function hasPrototypeProperty(object, name) {
  return! object.hasOwnProperty(name) && (namein object)
}
alert(hasPrototypeProperty(person, 'name')) // The true -- name attribute exists in the stereotype
Copy the code

C. In the for-in method, the value of Enumerated is true, but the value of Enumerated is false is masked

// With the object.keys () method, return an array of strings containing all enumerable attributes
var keys = Object.keys(Person.prototype)
alert(keys) // 'name, age, job, sayName'

. / / the Object getOwnPropertyNames () method, which returns all instance attribute (whether or not an enumerable)
var keys2 = Object.getOwnPropertyNames(Person.prototype)
alert(keys2) // 'constructor, name, age, job, sayName' -- constructor is not enumerable, but can be obtained by this method
Copy the code

2.3.4 Dynamic of prototype – Dynamic modification of prototype

A. Dynamic modification of the prototype: Any changes made to the prototype object are immediately reflected in the instance

// 1. Create object friend directly
var friend = new Person()

// 2. Then add the prototype object attribute sayHi to Person
Person.prototype.sayHi = function() {
  alert('Hi')}// 3. The friend object can use the newly added attribute value
friend.sayHi() //'Hi'
Copy the code

B. Note that if you use object literals to rewrite the prototype object, the constructor is severed from the original prototype object

Reason: Pointers in instances point to stereotypes, not constructors

// 1. Create object friend directly
var friend = new Person()

// 2. Then rewrite the prototype object for Person, including the attribute sayHi
Person.prototype = {
  constructor: Person, In the overwritten prototype object, set Person to the constructor value
  name: 'Nicholas'.age: 29.job: 'Doctor'.sayName: function() {
    alert(this.name)
  }
}

// 3. The object friend cannot use the new attribute values added by rewriting the prototype object.
// Reason: Person itself points to a new prototype object, not the original prototype object that friend points to
friend.sayHi() //error
Copy the code

2.3.5 Native objects also have prototype patterns

  1. Native objects: for example, Array, String, etc
  2. All native reference types, created from the above pattern, have archetypal patterns. Such as:Array.prototypeCan be found insort()Methods;String.prototypeCan be found insubstring()methods
  3. You can always add new prototype methods to native objects, but it’s not recommended

2.3.6 Make or Lose the prototype – Analysis of the problem of the prototype object: shared attribute

  1. Prototype’s most excellent feature is the ability to share. Some public methods and attributes can be used as prototype attributes for all instances to share
  2. However, Prototype also suffers from this shared nature: if there is a reference type in Prototype, the value of that attribute will interfere with each other in different instances
  3. Solution: Shared attributes in stereotypes, non-shared attributes (to avoid interference) in constructors ———— : a combination of constructor and stereotype patterns (see Section 2.4)
// Create the constructor Person
function Person() {}

// Create a prototype object that contains the reference type Friends (Array)
Person.prototype = {
  constructor: Person, In the overwritten prototype object, set Person to the constructor value
  name: 'Nicholas'.age: 29.job: 'Doctor'.friends: ['Sheldon'.'Raj']
  sayName: function() {
    alert(this.name)
  }
}

var person1 = new Person()
var person2 = new Person()

// In the person1 object, assign a value to the reference object attribute friends
person1.friends.push('Penny')

// Due to the shareability of prototype objects, all objects created through the Person() constructor have the same prototype properties, and reference objects in the prototype object point to the same value
// Problem: The shared attributes of two instances interfere with each other
alert(person1.friends) // 'Sheldon, Raj, Penny'
alert(person1.friends) // 'Sheldon, Raj, Penny'
alert(person1.fiends === person2.friends) // true
Copy the code

2.4 Combination of constructor pattern and stereotype pattern

  1. To solve the problem of mutual interference caused by the shared attributes of the prototype pattern, and to save the maximum memory space
  2. Take full advantage of the characteristics of the constructor pattern and the prototype pattern:
    1. Constructor pattern: Defines instance properties
    2. Stereotype pattern: Defines methods and shared properties
// Create the constructor person-non-shared attributes, especially reference types, as instance attributes
function Person(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.friends = ['Sheldon'.'Raj']}// Create a prototype object for Person - use shared properties, such as function, as prototype properties
Person.prototype = {
  constructor: Person, 
  sayName: function() {
    alert(this.name)
  }
}

var person1 = new Person('Nicholas'.29.'Doctor')
var person2 = new Person('Cindy'.28.'Developer')
person1.friends.push('Van')

// Instance attributes are different, but shared attributes point to the same pointer
alert(person1.friends === person2.friends) // false
alert(person1.sayName === person2.sayName) // true
Copy the code

2.5 Dynamic prototyping mode

  1. Dynamic prototyping pattern: Determine whether a prototype needs to be initialized by checking whether a method that should exist is valid
  2. Advantages: Memory space can be further optimized
  3. Note: When using the dynamic prototype pattern, you cannot rewrite the prototype using object literals. Rewriting a stereotype when an instance has already been created switches the relationship between the existing instance and the new stereotype
function Person(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.friends = ['Sheldon'.'Raj']
  
  SayName is added to the prototype only if it does not already exist
  // Add methods cannot use object literals,
  if (typeof this.sayName ! ='function') {
    Person.prototype.sayName = function() {
      alert(this.sayName)
    }
  }
}
Copy the code

2.6 The parasitic constructor pattern

  1. Parasitic constructor pattern: Creates a function that encapsulates the code that created the object and returns the newly created object
  2. Purpose: Used to add custom property methods to native objects (e.g. Array) without modifying the constructor of native objects (enhancements to native objects)
  3. The characteristics of:
    1. The creation method is similar to that of the factory pattern, but the function of the factory pattern is not a constructor. The wrapper function of this pattern can be called a constructor
    2. You can use the new operator
    3. The return object in the constructor can be overridden
    4. The constructor returns the same object as the one created outside the constructor
    5. You cannot determine the type of an object by instanceof
  4. This mode is not recommended!
// Example 1:
// Create a parasitic constructor pattern similar to the factory pattern
function Person(name, age, job) {
  var o = new Object(a)// Create an object
  o.name = name
  o.age = age
  o.job = job
  o.sayName = function () {
    alert(this.name)
  }
  return o // Return the object
  // If the return value is not set, the new object instance is returned by default; The return value can therefore be overridden in parasitic mode
}

// It can be created using the new operator, because the above methods can be considered constructors
var friend = new Person('Nicholas'.29.'doctor')
friend.sayName() // 'Nicholas'



// Example 2: Override and add properties to the Array method
Array constructors cannot be modified, but can be added to objects using parasitic mode
function SpecialArray () {
  var values = new Array(a)// Create an array object
  values.push.apply(values, arguments)  / / add value
  values.toPipedString = function () {  // Add method
    return this.join('|')}return values                         // Return an array
}

var colors = new SpecialArray('red'.'blue'.'green')
alert(colors.toPipedString())          // 'red|blue|green'
Copy the code

2.7 Secure constructor pattern

  1. Secure object: An object with no public attributes and methods that do not reference this, suitable for use in secure environments (this is disabled in safe mode)
  2. Similar to the parasitic mode creation method, the differences are as follows:
    1. The instance method of the newly created object does not reference this
    2. The constructor is not called using the new operator
  3. Note: You cannot use object literals to rewrite prototypes in secure mode, because object literals sever the relationship with the original constructor
function Person(name, age, job) {
  // Create an object
  var o = new Object() 
  o.name = name
  o.age = age
  o.job = job
  
  // Do not use this object
  o.sayName = function () {
    alert(name) 
  }
  
  // Return the object
  return o 
}

var friend = Person('Nicholas'.29.'doctor')

// There is no other way to get the name except by using the sayName in Person, so it is suitable for the security environment
friend.sayName() // 'Nicholas'
Copy the code

3. Summary

  1. Object basic properties: data properties, accessor properties
  2. How to create an object:
Object Creation mode Brief description
Factory pattern Encapsulate the object with a function, construct the entire object, and return the object at the end

Disadvantages: Can’t use the instanceof operator to determine the object type (because this method is not a constructor pattern)

Solution: Use constructors to solve the problem
The prototype pattern Use Prototype directly and take advantage of prototype’s shared features.

Disadvantages: When a reference object is stored in Prototype, it can cause the values of that prototype property to interfere with each other when different instances are created

Solution: Construct + prototype composite pattern
Dynamic prototype Determine if you need to initialize the prototype to further optimize the storage space by checking that a method that should exist is valid
Construct + prototype The constructor stores its own instance properties

The stereotype part stores shared properties (for example, function)
Parasitic constructor function Can be used to add custom methods or properties to native object (such as Array) enhancements without breaking the original object constructor

Disadvantages: You cannot use Instanceof to determine object types
Safe constructor The instance method of the newly created object does not reference this and runs safely in safe mode

The constructor is not called using the new operator
  1. The problem with prototypes: Reference objects in Prototype will change one place and change everywhere due to sharing
  2. Parasitic Nature: Enhancement – Add new custom properties to existing ones

Table of Notes:

JavaScript Learning (1) – A history of JavaScript

JavaScript Learning (2) – Basic syntax

Learning JavaScript (3)- Talk about prototype chains – 1. Variables

Learning JavaScript (3)- Talk about prototype chains – 2. Objects and prototypes

Learning JavaScript (3)- Talk about prototype chains – 3. Prototype chains and inheritance

JavaScript Learning (4)- Talk about closures

Github:

Github Notes link (continue to update, welcome star, please mark source for reprint)