Maison  >  Article  >  interface Web  >  Explication détaillée des objets de base JavaScript (organisés et partagés)

Explication détaillée des objets de base JavaScript (organisés et partagés)

WBOY
WBOYavant
2021-12-20 15:06:541959parcourir

Cet article vous apporte des connaissances pertinentes sur les objets en JavaScript. Les objets en JavaScript sont également des variables, mais les objets contiennent de nombreuses valeurs. J'espère que cela vous sera utile après avoir lu cet article.

Explication détaillée des objets de base JavaScript (organisés et partagés)

1. La base des objets

1.1 Types

JavaScript a six types de langage principaux :

chaîne, nombre, booléen, non défini, nul, objet

Types de base : chaîne, nombre, booléen, non défini, null ; le type primitif lui-même n’est pas un objet.

null

Mais null est parfois considéré comme un objet, et typeof null renverra un objet. En fait, null est un type de base. La raison en est que différents objets sont représentés comme binaires au niveau inférieur. En JavaScript, si les trois premiers chiffres du binaire sont 0, il sera jugé comme type d'objet, et null signifie que tous les 0, donc typeof renverra l'objet.

Sous-types d'objets spéciaux

Tableaux

Les tableaux sont également un type d'objet avec des comportements supplémentaires. L'organisation des tableaux est plus complexe que celle des objets ordinaires.

Fonction

La fonction est essentiellement la même qu'une fonction ordinaire, sauf qu'elle peut être appelée, vous pouvez donc utiliser des fonctions comme des objets.

1.2 Objet intégré

Chaîne
Numéro
Date
Booléen
Objet
Fonction
Tableau

1.3 Contenu

.a est appelé accès aux attributs, ['a'] est appelé accès opérateur.

//对象中的属性名始终是字符串
myobj={}

myobj[myobj]='bar'//赋值

myobj['[object object]'] //'bar'

1.4 Nom d'attribut calculable

es6 Ajout d'un nom d'attribut calculable, vous pouvez utiliser [] sous forme de texte pour envelopper une expression comme nom d'attribut

var perfix = 'foo'

var myobj={
    [perfix + 'bar'] :'hello'
}

myobj['foobar']//hello

1.5 Descripteur d'attribut

À partir d'es5, tous les attributs ont Avec l'attribut descripteur, par exemple, vous pouvez déterminer directement si l'attribut est lisible et inscriptible.

/*
 * 重要函数:
 * Object.getOwnPropertyDescriptor(..) //获取属性描述符
 * Object.defineProperty(..) //设置属性描述符
 */
 writeble(可读性)
 configurable(可配置性)
 enumerable (可枚举性)

1.6 Traversal

for in

for in peut être utilisé pour parcourir la liste d'attributs énumérables d'un objet (y compris la chaîne [[Prototype]]), et vous devez obtenir manuellement la valeur de l'attribut. Peut traverser des tableaux et des objets ordinaires

for of

es6 est nouveau et peut être utilisé pour parcourir les valeurs d'attribut des tableaux. La boucle for of demandera d'abord un objet itérateur à l'objet accédé, puis appellera ensuite. (de l'objet itérateur) pour parcourir toutes les valeurs de retour.
Les tableaux ont des @@iterators intégrés,

comment fonctionne for of ?

var arr = [1, 2, 3]
var it = arr[Symbol.iterator]()//迭代器对象
console.log(it.next());//{value: 1, done: false}
console.log(it.next());//{value: 2, done: false}
console.log(it.next());//{value: 3, done: false}
console.log(it.next());//{value: undefined, done: true}

/*
 * 用es6 的Symbol.iterator 来获取对象的迭代器内部属性。
 * @@iterator本身并不是一个迭代器对象,而是一个返回迭代器对象的函数。
 */

Comment un objet a-t-il un @@iterator intégré pour parcourir les valeurs des attributs ?

Étant donné que l'objet n'a pas d'@@iterator intégré, for...of traversal ne peut pas être automatiquement complété. Cependant, vous pouvez définir @@iterator pour tout objet que vous souhaitez parcourir, par exemple :

  var obj={
      a:1,b:2
  }
  
   Object.defineProperty(obj, Symbol.iterator, {
        enumerable: false,
        writable: false,
        configurable: true,
        value: function () {
            var self = this
            var idx = 0
            var ks = Object.keys(self)
            return {
                next: function () {
                    return {
                        value: self[ks[idx++]],
                        done: (idx > ks.length)
                    }
                }
            }
        }
    })
    
    //手动遍历
    var it = obj[Symbol.iterator]()//迭代器对象
    console.log(it.next());//{value: 1, done: false}
    console.log(it.next());//{value: 2, done: false}
    console.log(it.next());//{value: undefined, done: true}

   //for of 遍历
    for (const v of obj) {
        console.log(v);
    }
    //2
    //3

Autres fonctions de traversée de tableau

 /*
 forEach:会遍历所有并忽略返回值
 some:会一直运行到回调函数返回 true(或者"真"值)
 every:会一直运行到回调函数返回 false(或者"假"值)
 map:
 filter:返回满足条件的值
 reduce:
 some和every 和for的break语句类似,会提前终止遍历
 */

2. Objets de classe mixtes

Questions d'entretien

1.

Connaissances connexes

Types de base et types de référence

Comme mentionné ci-dessus, il existe six types de langage principaux en JavaScript : chaîne, nombre, booléen, nul, non défini et objet ; les cinq premiers sont des types de base et le dernier objet est ; Type de référence.

Les méthodes de stockage des variables JavaScript : stack et tas

Stack : alloue automatiquement de l'espace mémoire et est automatiquement libéré par le système. Il stocke les valeurs de type de base et les adresses de type de référence


Heap : allocation dynamique La taille de la mémoire est variable et ne sera pas libéré automatiquement. Les valeurs de type référence y sont stockées

La plus grande différence entre les types de base et les types de référence est la différence entre le passage par valeur et le passage par adresse

Les types de base utilisent le passage de valeur ; stocker l'adresse dans la mémoire de la pile. Attribuer une valeur à la variable reçue.

Que sont la copie superficielle et la copie profonde ?

Copie superficielle : Une copie superficielle d'un objet copiera l'objet « principal », mais ne copiera pas les objets à l'intérieur de l'objet. L'« objet intérieur » est partagé entre l'objet original et sa copie.

Copie approfondie : une copie approfondie d'un objet copie non seulement chaque attribut de l'objet d'origine un par un, mais copie également de manière récursive les objets contenus dans chaque attribut de l'objet d'origine vers le nouvel objet en utilisant la méthode de copie approfondie , donc les modifications apportées à un objet n'affectent pas l'autre objet.
Par exemple :

var anotherObject={
    b:"b"
}

var anotherArray=[]

var myObject={
    a:'a',
    b:anotherObject, //引用,不是副本
    c:anotherArray //另外一个引用
}

anotherArray.push(anotherObject,myObject)

/*
  如何准确的复制 myObject?
 浅复制 myObject,就是复制出 新对象中的 a 的值会复制出对象中a 的值,也就是 'a',
 但是对象中的 b、c两个属性其实只是三个引用,新对象的b、c属性和旧对象的是一样的。
 
 深复制 myObject,除了复制 myObject 以外还会复制 anotherObject 和 anotherArray。
 但是这里深复制 myObject会出现一个问题,anotherArray 引用 anotherObject 和 myObject,
 所以又需要复制 myObject,这样就会由于循环引用导致死循环。
 后面会介绍如何处理这种情况。
*/
Comment implémenter une copie superficielle

object

object.assign(), spread Operator (...)

var obj1 = {x: 1, y: 2}
var obj2 = Object.assign({}, obj1);
console.log(obj1) //{x: 1, y: 2}
console.log(obj2) //{x: 1, y: 2}
obj2.x = 2; //修改obj2.x
console.log(obj1) //{x: 1, y: 2}
console.log(obj2) //{x: 2, y: 2}

var obj1 = {
    x: 1, 
    y: {
        m: 1
    }
};
var obj2 = Object.assign({}, obj1);
console.log(obj1) //{x: 1, y: {m: 1}}
console.log(obj2) //{x: 1, y: {m: 1}}
obj2.y.m = 2; //修改obj2.y.m
console.log(obj1) //{x: 1, y: {m: 2}}
console.log(obj2) //{x: 2, y: {m: 2}}
Array

slice(), concat, Array.from(), spread symbole d'opérateur (...), concat, for loop

var arr1 = [1, 2, [3, 4]], arr2 = arr1.slice();
console.log(arr1); //[1, 2, [3, 4]]
console.log(arr2); //[1, 2, [3, 4]]

arr2[0] = 2 
arr2[2][1] = 5; 
console.log(arr1); //[1, 2, [3, 5]]
console.log(arr2); //[2, 2, [3, 5]]
Comment implémenter la copie profonde

JSON.parse(JSON.stringify(obj))

Pendant le processus de sérialisation de JSON.stringify(), fonctions arbitraires non définies et les symboles Les valeurs seront ignorées lors de la sérialisation (lors de leur apparition dans les valeurs de propriété d'objets non-tableaux) ou converties en null (lors de leur apparition dans des tableaux).

var obj1 = {
    x: 1, 
    y: {
        m: 1
    },
    a:undefined,
    b:function(a,b){
      return a+b
    },
    c:Symbol("foo")
};
var obj2 = JSON.parse(JSON.stringify(obj1));

console.log(obj1) //{x: 1, y: {m: 1}, a: undefined, b: ƒ, c: Symbol(foo)}
console.log(obj2) //{x: 1, y: {m: 1}}
obj2.y.m = 2; //修改obj2.y.m
console.log(obj1) //{x: 1, y: {m: 1}, a: undefined, b: ƒ, c: Symbol(foo)}
console.log(obj2) //{x: 2, y: {m: 2}}
Implémentation simple de la fonction de copie profonde

function deepClone(obj){
  let result = Array.isArray(obj)?[]:{};
  if(obj && typeof obj === "object"){
    for(let key in obj){
      if(obj.hasOwnProperty(key)){
        if(obj[key] && typeof obj[key] === "object"){
          result[key] = deepClone(obj[key]);
        }else{
          result[key] = obj[key];
        }
      }
    }
  }
  return result;
}

var obj1 = {
    x: {
        m: 1
    },
    y: undefined,
    z: function add(z1, z2) {
        return z1 + z2
    },
    a: Symbol("foo"),
    b: [1,2,3,4,5],
    c: null
};
var obj2 = deepClone(obj1);
obj2.x.m = 2;
obj2.b[0] = 2;
console.log(obj1);
console.log(obj2);

//obj1
{
a: Symbol(foo)
b: (5) [1, 2, 3, 4, 5]
c: null
x: {m: 1}
y: undefined
z: ƒ add(z1, z2)
}

//obj2
{
a: Symbol(foo)
b: (5) [2, 2, 3, 4, 5]
c: null
x: {m: 2}
y: undefined
z: ƒ add(z1, z2)
}

La méthode de copie profonde ci-dessus rencontre une référence circulaire et tombera dans un processus récursif cyclique, provoquant l'explosion de la pile. Des améliorations sont donc nécessaires.

Amélioration de la fonction de copie profonde (empêchant la récursion de boucle)

Pour résoudre le problème d'explosion de pile due à la récursion de boucle, il vous suffit de déterminer si un champ d'un objet fait référence à cet objet ou à un parent de cet objet.

function deepClone(obj, parent = null){ // 改进(1)
  let result = Array.isArray(obj)?[]:{};
  let _parent = parent;  // 改进(2)
  while(_parent){ // 改进(3)
    if(_parent.originalParent === obj){
      return _parent.currentParent;
    }
    _parent = _parent.parent;
  }
  if(obj && typeof obj === "object"){
    for(let key in obj){
      if(obj.hasOwnProperty(key)){
        if(obj[key] && typeof obj[key] === "object"){
          result[key] = deepClone(obj[key],{ // 改进(4)
            originalParent: obj,
            currentParent: result,
            parent: parent
          });
        }else{
          result[key] = obj[key];
        }
      }
    }
  }
  return result;
}

// 调试用
var obj1 = {
    x: 1, 
    y: 2
};
obj1.z = obj1;
var obj2 = deepClone(obj1);
console.log(obj1); 
console.log(obj2);
La version finale de la fonction de copie approfondie (prend en charge les types de données de base, les chaînes de prototypes, RegExp, les types de date)

function deepClone(obj, parent = null){ 
  let result; // 最后的返回结果

  let _parent = parent; // 防止循环引用
  while(_parent){
    if(_parent.originalParent === obj){
      return _parent.currentParent;
    }
    _parent = _parent.parent;
  }
  
  if(obj && typeof obj === "object"){ // 返回引用数据类型(null已被判断条件排除))
    if(obj instanceof RegExp){ // RegExp类型
      result = new RegExp(obj.source, obj.flags)
    }else if(obj instanceof Date){ // Date类型
      result = new Date(obj.getTime());
    }else{
      if(obj instanceof Array){ // Array类型
        result = []
      }else{ // Object类型,继承原型链
        let proto = Object.getPrototypeOf(obj);
        result = Object.create(proto);
      }
      for(let key in obj){ // Array类型 与 Object类型 的深拷贝
        if(obj.hasOwnProperty(key)){
          if(obj[key] && typeof obj[key] === "object"){
            result[key] = deepClone(obj[key],{ 
              originalParent: obj,
              currentParent: result,
              parent: parent
            });
          }else{
            result[key] = obj[key];
          }
        }
      }
    }
  }else{ // 返回基本数据类型与Function类型,因为Function不需要深拷贝
    return obj
  }
  return result;
}

// 调试用
function construct(){
    this.a = 1,
    this.b = {
        x:2,
        y:3,
        z:[4,5,[6]]
    },
    this.c = [7,8,[9,10]],
    this.d = new Date(),
    this.e = /abc/ig,
    this.f = function(a,b){
        return a+b
    },
    this.g = null,
    this.h = undefined,
    this.i = "hello",
    this.j = Symbol("foo")
}
construct.prototype.str = "I'm prototype"
var obj1 = new construct()
obj1.k = obj1
obj2 = deepClone(obj1)
obj2.b.x = 999
obj2.c[0] = 666
console.log(obj1)
console.log(obj2)
console.log(obj1.str)
console.log(obj2.str)

Remarque : copie approfondie du type de fonction :

bind() : utilisez fn.bind() pour faire une copie complète de la fonction, mais elle ne peut pas être utilisée à cause du problème de ce pointeur

eval(fn.toString()) : ne prend en charge que les fonctions fléchées, ordinaires ; function function fn() {} n'est pas applicable ;

new Function(arg1, arg2,…,function_body) : Les paramètres et le corps de la fonction doivent être extraits ;

PS : Généralement, il n'est pas nécessaire de copier en profondeur Function.

【Recommandations associées : Tutoriel d'apprentissage Javascript

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer