ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript基本オブジェクトの詳しい解説(整理・共有)

JavaScript基本オブジェクトの詳しい解説(整理・共有)

WBOY
WBOY転載
2021-12-20 15:06:541959ブラウズ

この記事では、JavaScript のオブジェクトに関する関連知識を提供します。JavaScript のオブジェクトも変数ですが、オブジェクトには多くの値が含まれています。この記事を読んだ後で役立つことを願っています。

JavaScript基本オブジェクトの詳しい解説(整理・共有)

1. オブジェクトの基礎

1.1 型

JavaScript には 6 つの主要な言語型があります:

string、数値、ブール値、未定義、ヌル、オブジェクト

基本タイプ: 文字列、数値、ブール値、未定義、ヌル; 基本タイプ自体はオブジェクトではありません。

null

ただし、null はオブジェクトとみなされ、typeof null はオブジェクトを返します。実際、null は基本型です。これは、最下位レベルで異なるオブジェクトがバイナリで表現されているためで、JavaScriptではバイナリの最初の3桁が0であればオブジェクトの種類と判断され、nullはすべて0を意味します。なので、typeof はオブジェクトを返します。

特別なオブジェクトのサブタイプ

Array

Array も、いくつかの追加の動作を持つオブジェクトのタイプです。配列の構成は通常のオブジェクトよりも複雑です。

関数

関数は、呼び出すことができる点を除いて、基本的に通常の関数と同じであるため、オブジェクトと同じ方法で関数を操作できます。

1.2 組み込みオブジェクト

String
Number
Date
Boolean
Object
Function
Array

1.3 内容

.a は属性アクセスと呼ばれ、['a'] は演算子アクセスと呼ばれます。

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

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

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

1.4 計算可能な属性名

es6 計算可能な属性名を追加します。テキスト形式で [] を使用して式を属性名としてラップできます

var perfix = 'foo'

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

myobj['foobar']//hello

1.5属性記述子

es5 以降、すべての属性には属性記述子があり、たとえば、属性が読み取り可能か書き込み可能かどうかを直接判断できます。

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

1.6 Traversal

for in

for in は、オブジェクトの列挙可能な属性リスト ([[Prototype]] チェーンを含む) を走査するために使用できます。属性値を手動で取得する必要があります。配列と通常のオブジェクトを走査可能

for of

es6 は新しく、配列の属性値を走査するために使用できます。for ループは最初に反復子オブジェクトを要求します。アクセスされたオブジェクトを取得し、イテレータ オブジェクトの next() メソッドを呼び出してすべての戻り値を反復処理します。
配列には @@ イテレータが組み込まれていますが、

for はどのように機能するのでしょうか?

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本身并不是一个迭代器对象,而是一个返回迭代器对象的函数。
 */

オブジェクトには、属性の値をトラバースするための組み込み @@iterator がどのようにしてあるのでしょうか?

オブジェクトには組み込み @@iterator がないため、for...of トラバーサルを自動的に完了できません。ただし、走査したい任意のオブジェクトに対して @@iterator を定義できます (例:

  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

その他の配列走査関数

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

2. 混合クラス オブジェクト

インタビューの質問

1. オブジェクトの深いコピーと浅いコピー

関連知識

基本型と参照型

前述したように、JavaScript の主な言語型は次のとおりです。文字列、数値、ブール値、null、未定義、オブジェクトの 6 つの型があり、最初の 5 つは基本型で、最後のオブジェクトは参照型です。

JavaScript変数の格納方法:スタックとヒープ

スタック:メモリ空間を自動的に割り当て、システムによって自動的に解放され、基本型の値と参照型が格納されます。

ヒープ: 動的に割り当てられるメモリ。サイズは可変で、自動的には解放されません。参照型の値はそこに格納されます。

基本型と参照型の最大の違いは、値渡しとアドレス渡しの違いです。

基本型は値渡しを使用し、参照型は値渡しを使用します。アドレス(ポインタ)の受け渡し スタックメモリに格納されているアドレスを受け取った変数に代入します。

シャローコピーとディープコピーとは何ですか?

浅いコピー: オブジェクトの浅いコピーは「メイン」オブジェクトをコピーしますが、オブジェクト内のオブジェクトはコピーしません。 「内部オブジェクト」は、元のオブジェクトとそのコピーの間で共有されます。

ディープ コピー: オブジェクトのディープ コピーは、元のオブジェクトの各属性を 1 つずつコピーするだけでなく、オブジェクトの各属性に含まれるオブジェクトも深くコピーします。メソッドは新しいオブジェクトに再帰的にコピーされるため、一方のオブジェクトを変更しても、もう一方のオブジェクトには影響しません。

例:

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,这样就会由于循环引用导致死循环。
 后面会介绍如何处理这种情况。
*/

浅いコピーの実装方法

object

object.assign()、スプレッド演算子 (…)

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()、スプレッド演算子 (...)、concat、for ループ

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

ディープ コピーの実装方法

JSON.parse(JSON.stringify(obj))

JSON.stringify() のシリアル化プロセス中、未定義の任意の関数とシンボル値はシリアル化プロセス中に無視されます (非定義で表示されます)。配列オブジェクトのプロパティ値にある場合)、または null に変換されます (配列に出現する場合)。

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

ディープ コピー関数の簡単な実装

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

上記のディープ コピー メソッドは循環参照に遭遇し、循環再帰プロセスに陥り、スタックの爆発を引き起こします。したがって、改善が必要です。

ディープコピー機能の改善(ループ再帰防止)

ループ再帰によるスタック爆発の問題を解決するには、オブジェクトのフィールドがこのオブジェクトを参照しているかどうかを判断するだけで済みます。このオブジェクト レベルの親であればどれでも十分です。

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

ディープ コピー関数の最終バージョン (基本的なデータ型、プロトタイプ チェーン、RegExp、および日付型をサポート)

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)

注: 関数タイプのディープ コピー:

bind(): fn.bind() を使用して関数のディープ コピーを作成しますが、このポインタに問題があるため使用できません;

eval(fn.toString()) : アロー関数のみがサポートされています。通常の関数 function fn(){} は適用できません;

new Function(arg1,arg2,...,function_body): パラメータと関数本体を抽出する必要があります;

PS: 通常、Function のディープ コピーは必要ありません。

[関連する推奨事項: JavaScript 学習チュートリアル]

以上がJavaScript基本オブジェクトの詳しい解説(整理・共有)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。