Maison  >  Article  >  interface Web  >  Explication détaillée du passage et de la copie en js

Explication détaillée du passage et de la copie en js

小云云
小云云original
2018-03-13 18:15:581143parcourir

Nous savons que js a plusieurs types de données de base et d'autres types de données complexes, notamment (objets, tableaux, fonctions). L'affectation des types de données de base est en fait une copie de la valeur, et nous l'appelons le transfert de valeur, et la variable après. affectation Il n'y a pas d'autre relation avec la variable d'origine sauf que la valeur est égale.

let x = 666
let y = x
let m = 'abc'
let n = m
y = 888
n = 'def'
console.log(x, y)//666,888
console.log(m, n)//'abc','def'

Le transfert de types de données complexes n'est pas comme ça, car lorsqu'une variable est liée à un type de données complexe, ce qui est enregistré n'est pas la valeur des données complexes, mais le stockage des données . Une information d'adresse, lors de l'affectation de cette variable à une autre variable, seule l'adresse est transmise. Les deux variables pointent en fait vers une information de données. Lorsqu'une variable est modifiée, les autres variables seront affectées. Cette méthode de transfert est appelée passage. référence

let obj1 = {
    a : '1',
    b : 2
} 
let obj2 = obj1
obj2.b = 3
console.log(obj1,obj2)//{a: "1", b: 3},{a: "1", b: 3}

copie

Nous savons que l'affectation de types de données complexes est transmise par référence et que les variables avant et après l'affectation s'influencent souvent dans les projets réels. Nous ne voulons pas cela, par exemple :

Nous utilisons des données (un tableau) à deux endroits dans une vue. L'un est qu'une liste n'a besoin que d'afficher les données dans l'ordre, et l'autre est qu'un. chart nécessite Après avoir inversé les données puis effectué le traitement des données, nous aurons un problème si la première liste après data.reverse() est également dans l'ordre inverse, ce n'est pas ce que nous voulons. Nous avons besoin d'une méthode pour copier uniquement la valeur. du tableau.Et l'adresse des données de ce nouveau tableau est différente de celle du tableau d'origine. Cette méthode de copie est appelée copie de tableau.

let obj1 = {a:1, b:{c:2}}
let shallowCopy = (src)=> {
    let dst = {}
    for (let prop in src) {
        if (src.hasOwnProperty(prop)) {
          dst[prop] = src[prop]
        }
    }
    return dst
}
let obj2 = shallowCopy(obj1)
console.log(obj1,obj2) //@1
obj1.a = 6
console.log(obj2.a) //@2
obj2.b.c = 666
console.log(obj1.b.c) //@3
obj2.b = {
    c: 888
}
console.log(obj1.b.c) //@4

On peut voir dans l'exemple ci-dessus que l'attribut de première couche d'obj1 copie la valeur de l'attribut et n'hérite pas de la copie de l'adresse, mais la deuxième couche est que l'attribut b partage un adresse mémoire, qui est une copie superficielle. , mais à @4, obj1 n'a pas été affecté par obj2, car l'attribut b est un objet. Pour ce type de réaffectation par référence, l'ordinateur réaffectera une nouvelle mémoire pour stocker les données et enregistrer. informations d'adresse, donc à l'heure actuelle, obj1.b.c et obj2.b.c ne sont plus une valeur d'attribut de l'enregistrement

peut également être comprise comme : la copie est destinée au transfert. L'affectation directe de types de données complexes est un transfert par référence. et ne peut pas être appelé copie. La copie est une simple sauvegarde des données originales. Les informations d'adresse mémoire des données ne sont pas exactement les mêmes. En effet, la copie est également divisée en copie superficielle et copie approfondie.

Une copie non imbriquée d'un type de données complexe signifie que seule la première couche d'informations sur les données est copiée, ce qui est une copie superficielle. Si la première couche de données a un type de données complexe, elle est quand même transmise. par référence. , ce qui est copié sont toujours les informations d'adresse, et la copie imbriquée multicouche des objets de tableau, etc. implémentée par d'autres méthodes est une copie complète.

Voyons comment les tableaux et les objets implémentent des copies profondes et superficielles :

Copie de tableaux

  • méthode slice

    let arr1 = [1,2,[3,4]]
    let arr2 = arr1.slice(0)
    arr2[2].push(5)
    arr2.push(6)
    console.log(arr1,arr2)
  • méthode de concat

    let arr1 = [1,2,[3,4]]
    let arr2 = arr1.concat()
    arr2[2].push(5)
    arr2.push(6)
    console.log(arr1,arr2)
  • for boucle

    let arr1 = [1,2,[3,4]]
    let arr2 = []
    for(let i = 0; i<arr1.length; i++){
        arr2.push(arr1[i])
    }
    arr2[2].push(5)
    arr2.push(6)
    console.log(arr1,arr2)
  • …opérateur

    let arr1 = [1,2,[3,4]]
    let [...arr2] = arr1
    arr2[2].push(5)
    arr2.push(6)
    console.log(arr1,arr2)

Les copies des quatre types de tableaux ci-dessus sont toutes des copies superficielles. Pour réaliser la copie profonde du tableau, elle doit être implémentée de manière récursive

let deepClone = (src)=> {
    let result
    (src instanceof Array) ? (result = []) :(result = {})
    for (let key in src) {
        result[key] = (typeof src[key] === 'object') ? deepClone(src[key]) : src[key]//数组和对象的type都是object
    }
    return result
}   
let arr1 = [1,2,[3,4]]
let arr2 = deepClone(arr1)
arr2[2].push(5)
arr2.push(6)
console.log(arr1,arr2)

Vous pouvez trouver. la méthode arr1[2] Contrairement à arr2[2], la même méthode de copie profonde ci-dessus est également applicable à la copie de l'objet

objet

  • boucle for universelle

    let obj1 = {a:1,b:{c:2}}
    let obj2 = {}
    for(let key in obj1){
        obj2[key] = obj1[key]
    }
    obj1.b.c = 6
    console.log(obj1,obj2)
  • …Opérateur

    let obj1 = {a:1,b:{c:2}}
    let {...obj2} = obj1
    obj1.b.c = 6
    console.log(obj1,obj2)
  • Object.assign()

    let obj1 = {a:1,b:{c:2}}
    let obj2 = Object.assign({},obj1)
    obj1.b.c = 6
    console.log(obj1,obj2)

Les 3 types ci-dessus La méthode est une copie superficielle de l'objet. Voici deux méthodes de copie profonde de l'objet :

  • convertir en chaîne puis revenir en objet

    let obj1 = {a:1,b:{c:2}}
    let obj2 = JSON.parse(JSON.stringify(obj1))
    obj1.b.c = 6
    console.log(obj1,obj2)
  • La méthode deepClone est la méthode deepClone du tableau ci-dessus

Concepts associés

Fonction pure

Donné une entrée dans une fonction, elle renvoie une fonction unique. Une fonction qui a une sortie et n'a aucun impact sur l'environnement externe est appelée une fonction pure. Les variables définies à l'intérieur seront recyclées par le mécanisme de récupération de place après le retour de la fonction.

Mais si le paramètre de la fonction est un tableau, un objet ou une fonction, une référence est transmise et l'opération affectera les données d'origine. La fonction écrite de cette manière aura des effets accessoires et la rendra lisible. La sexualité devient faible.

Le moyen de réduire l'impact est de faire une copie complète des paramètres entrants et de les affecter à une nouvelle variable pour éviter que les paramètres d'origine ne soient falsifiés.

Regardons un exemple de fonction pure :

let pureFunc = (animal)=> {
    let newAnimal = JSON.parse(JSON.stringify(animal))
    newAnimal.type = &#39;cat&#39;
    newAnimal.name = &#39;Miao&#39;
    return newAnimal
}

let wang = {
    type: &#39;dog&#39;,
    name: &#39;Wang&#39;
}

let miao = pureFunc(wang)
console.log(wang,miao)
A travers l'exemple ci-dessus, vous pouvez voir que wang n'a pas été modifié par une fonction pure.

Repensons à l'exemple suivant. Si vous y répondez correctement, cela signifie que vous avez une compréhension approfondie de ce dont parle cet article (rappelez-le à tout le monde—>réaffectation des références)

let afterChange = (obj)=>{
    obj.a = 6
    obj = {
        a: 8,
        b: 9
    }
    return obj
}
let objIns = {
    a: 1,
    b: 2
}

let objIns2 = afterChange(objIns)
console.log(objIns, objIns2)
Ce qui précède est ma compréhension de la référence et de la transmission de js. Veuillez me pardonner s'il y a un caractère inapproprié. Merci !

Vous pouvez également lire d'autres articles pour approfondir votre compréhension. Je recommande cette valeur explicative par rapport à la référence en Javascript.

Nous rencontrons souvent la situation d'assigner une variable à une autre variable dans le projet. Il s'agit en fait du transfert de js, mais différents types de données se comportent différemment après l'affectation. Étudions-le ensemble.

传递

我们知道js有几种基本的数据类型和其他的复杂数据类型包括(对象,数组,函数),基本数据类型的赋值其实就是值的拷贝,我们称之为值传递,赋值后的变量和原来的变量除了值相等之外并无其他关联

let x = 666
let y = x
let m = &#39;abc&#39;
let n = m
y = 888
n = &#39;def&#39;
console.log(x, y)//666,888
console.log(m, n)//&#39;abc&#39;,&#39;def&#39;

复杂数据类型的传递并不是这样子的,因为将一个变量绑定到一个复杂数据类型的时候记录的并不是这个复杂数据的值,而是存放了这个数据的一个地址信息,当将这个变量赋值给另一个变量的时候只是将地址传递了过去,这两个变量指向的其实是一个数据信息,当改变任意一个变量的时候,另外的变量都会受到影响,这种传递方式我们称之为引用传递

let obj1 = {
    a : &#39;1&#39;,
    b : 2
} 
let obj2 = obj1
obj2.b = 3
console.log(obj1,obj2)//{a: "1", b: 3},{a: "1", b: 3}

拷贝

我们知道复杂数据类型的赋值是引用传递,赋值前后的变量会相互影响,在实际项目中我们经常不希望这样子,譬如:

我们在一个view里面有两处用到了data(是一个Array),其一是一个list只要把data按顺序展示即可,其二是一个chart需要把data逆序之后再做数据处理,这时候我们就犯难了,如果直接data.reverse()之后第一处的列表也是逆序的了,这不是我们想见的,我们需要一个方法只复制数组的值,并且这个新数组和原数组的数据地址并不一样,这种复制方式我们称为数组的拷贝。

let obj1 = {a:1, b:{c:2}}
let shallowCopy = (src)=> {
    let dst = {}
    for (let prop in src) {
        if (src.hasOwnProperty(prop)) {
          dst[prop] = src[prop]
        }
    }
    return dst
}
let obj2 = shallowCopy(obj1)
console.log(obj1,obj2) //@1
obj1.a = 6
console.log(obj2.a) //@2
obj2.b.c = 666
console.log(obj1.b.c) //@3
obj2.b = {
    c: 888
}
console.log(obj1.b.c) //@4

上面的例子可以看出来obj1的第一层属性是复制属性值,没有继承地址的拷贝,但是第二层就是b属性确实共享一块内存地址的,这就是浅拷贝,但是在@4处obj1却没有收到obj2的影响,是因为属性b是一个对象,这种引用传递的重新赋值,计算机会重新分配一块新的内存来存放数据和记录地址信息,所以这时obj1.b.c和obj2.b.c已经不是记录的一个属性值了

也可以理解为:拷贝是之于传递的,直接对复杂数据类型进行赋值是引用传递,不能称之为拷贝,拷贝是对原数据的单纯的数据备份,数据的内存地址信息并不完全一样,这是因为拷贝还分为浅拷贝和深拷贝。

对复杂数据类型的非嵌套的拷贝,就是只拷贝第一层的数据信息的拷贝是浅拷贝,如果第一层的数据有复杂数据类型,则依然采用引用传递的方式,复制的仍然是地址信息,通过其他方式实现的数组对象等的多层嵌套拷贝就是深拷贝。

下面我们再来看下数组和对象如何来实现深浅拷贝:

数组的拷贝

  • slice方法

    let arr1 = [1,2,[3,4]]
    let arr2 = arr1.slice(0)
    arr2[2].push(5)
    arr2.push(6)
    console.log(arr1,arr2)
  • concat方法

    let arr1 = [1,2,[3,4]]
    let arr2 = arr1.concat()
    arr2[2].push(5)
    arr2.push(6)
    console.log(arr1,arr2)
  • for循环

    let arr1 = [1,2,[3,4]]
    let arr2 = []
    for(let i = 0; i32bfcfa2ad8973d97a5666fc1acc4e11 {
        let result
        (src instanceof Array) ? (result = []) :(result = {})
        for (let key in src) {
            result[key] = (typeof src[key] === 'object') ? deepClone(src[key]) : src[key]//数组和对象的type都是object
        }
        return result
    }   
    let arr1 = [1,2,[3,4]]
    let arr2 = deepClone(arr1)
    arr2[2].push(5)
    arr2.push(6)
    console.log(arr1,arr2)

    可以发现用方面的方法arr1[2]和arr2[2]不一样,同样上面的深拷贝的方法也适用于对象

    对象的拷贝

    • 万能的for循环

      let obj1 = {a:1,b:{c:2}}
      let obj2 = {}
      for(let key in obj1){
          obj2[key] = obj1[key]
      }
      obj1.b.c = 6
      console.log(obj1,obj2)
    • …运算符

      let obj1 = {a:1,b:{c:2}}
      let {...obj2} = obj1
      obj1.b.c = 6
      console.log(obj1,obj2)
    • Object.assign()

      let obj1 = {a:1,b:{c:2}}
      let obj2 = Object.assign({},obj1)
      obj1.b.c = 6
      console.log(obj1,obj2)

    上面3种方法是对象的浅拷贝,再介绍2种对象的深拷贝的方法:

    • 转为字符串再转回对象

      let obj1 = {a:1,b:{c:2}}
      let obj2 = JSON.parse(JSON.stringify(obj1))
      obj1.b.c = 6
      console.log(obj1,obj2)
    • deepClone方法,就是上面的数组的deepClone方法

    相关的概念

    纯函数

    给定函数一个输入返回一个唯一的输出,并且不对外部环境附带任何影响的函数我们称为纯函数,其内定义的变量在函数返回后都会被垃圾回收机制回收掉。

    但是如果函数的参数是数组、对象或函数时,传入的是一个引用,对其操作会影响到原来的数据,这样子写的函数会产生附带的影响,使得可读性变低。

    降低影响的方式就是对传入参数进行深度拷贝,并赋给一个新的变量,方式原来的参数被篡改。

    我们来看一个纯函数的例子:

    let pureFunc = (animal)=> {
        let newAnimal = JSON.parse(JSON.stringify(animal))
        newAnimal.type = &#39;cat&#39;
        newAnimal.name = &#39;Miao&#39;
        return newAnimal
    }
    
    let wang = {
        type: &#39;dog&#39;,
        name: &#39;Wang&#39;
    }
    
    let miao = pureFunc(wang)
    console.log(wang,miao)

    通过上面的例子可以看到wang并没有被纯函数所改变值。

    大家再来思考一下下面的例子,如果答对了说明你已经对本文所讲的东西有了很深刻的理解了(提醒大家一下—>引用的重新赋值)

    let afterChange = (obj)=>{
        obj.a = 6
        obj = {
            a: 8,
            b: 9
        }
        return obj
    }
    let objIns = {
        a: 1,
        b: 2
    }
    
    let objIns2 = afterChange(objIns)
    console.log(objIns, objIns2)

    以上就是我对js的引用和传递的理解,如有不当之处敬请谅解,Thanks!

    相关推荐:

    js函数的按值传递参数

    JavaScript参数传递图解教程

    js函数参数的按值传递解释

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:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn