객체가 JavaScript의 Array 유형에 속하는지 여부를 판단하는 4 가지 방법과 그이면의 원칙과 제한 사항

머리말

Array.isArray가 오늘날 JavaScript에서 객체가 Array 유형에 속하는지 여부를 판단하는 첫 번째 선택이라는 것은 의심 할 여지가 없지만이 기사의 나머지 메서드와 그 뒤에있는 원칙과 제한 사항을 이해해야한다고 생각합니다. 대부분은 자바 스크립트에 있기 때문에 레퍼런스 타입은 Array 타입과 같은 isArray 판단 방법을 제공하지 않으며, 이때 나머지 방법을 사용하여 추론을 도출 할 필요가 있습니다.

오리 모델

동물이 오리처럼 걷고 오리처럼 부를 때 그것은 오리입니다.
객체가 Array 유형의 속성 (예 : 'splice', 'join'또는 'length')을 포함하는 경우 해당 객체는 Array 유형에 속합니다.

prototypejs의 1.6.0.3 버전 은이 로직을 사용하며 코드는 다음과 같습니다.

isArray: function(object) {
    
    
    return object != null && typeof object == "object" && 'splice' in object && 'join' in object;
}

하지만 덕 모드에는 문제가 있는데, 동물이 오리처럼 걷다가 오리처럼 소리를 지르면 오리가 될뿐만 아니라 '도날드 덕'이 될 수도 있습니다.
영상

이것은 Array 유형의 두 가지 속성 인 " splice" 및 " join"포함하는 객체 와 같습니다 . Array 유형 외에도 Person 유형이 될 수도 있습니다.

function isArray(object) {
    
    
    return object != null && typeof object == "object" && 'splice' in object && 'join' in object;
}

function Person(){
    
    
    /**
    code
    */
}

Person.prototype.splice = function(){
    
    
    /**
    code
    */
}

Person.prototype.join = function(){
    
    
    /**
    code
    */
}

let p = new Person();

let isArr = isArray(p);

console.log('isArray : ' + isArr);//isArray : true

duck 모드는 jqueryisArrayLike 메소드 와 같이 ' like Array ' 판단 하는 데 더 많이 사용 되며 코드는 다음과 같습니다.

function isArrayLike( obj ) {
    
    

	var length = !!obj && obj.length,
		type = toType( obj );

	if ( typeof obj === "function" || isWindow( obj ) ) {
    
    
		return false;
	}

	return type === "array" || length === 0 ||
		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}

obj 두 가지 판단 논리 길이 === 0길이 유형 === "number"&& length> 0 && (length-1) 은 객체가 'length'속성을 통해 ' like Array '에 부합하는지 여부를 판단하는 것 입니다.

instanceof 키워드

instanceof 키워드의 내용은 " 사용 시나리오에 대한 심층 이해와 typeof 및 instanceof의 예방 조치에 대한 자세한 설명이므로 여기에서는 간단한 설명 만 제공합니다. instanceof 키워드는 Array 함수의 프로토 타입 속성 값이 객체의 프로토 타입 체인에 있는지 여부를 확인하는 데 사용됩니다. 존재하는 경우 객체가 Array 유형이고 그렇지 않은 경우 그렇지 않음을 의미합니다.

그러나 instanceof는 완전히 신뢰할 수 없습니다. 예를 들어 instanceof의 판단 결과는 Symbol.hasInstance 속성의 영향을받을 수 있습니다.

function Person(){
    
    
}

Object.defineProperty(Person,Symbol.hasInstance,{
    
    
    value : function(){
    
    
        return false;
    }
})

let p = new Person();

p instanceof Person;//false

데이터 및 유형이 전역 변수에 속하지 않는 경우 instanceof의 판단 결과에도 영향을 미칠 수 있습니다. 예를 들어, 이제 두 개의 html 파일 인 main.html 및 iframe.html을 정의하면 코드는 다음과 같습니다.

main.html

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title>main</title>      
    </head>
    <body>
        <iframe src="./iframe.html"></iframe>
        <script type="text/javascript">
            window.onload = function(){
     
     
                console.log('document.domain : ' + document.domain);
                let mainArr = [1,2,3,4,5];
                console.log('mainArr instanceof Array : ' + (mainArr instanceof Array));//
                let iframe = document.documentElement.getElementsByTagName('iframe')[0];
                let iframeWin = iframe.contentWindow.window;
                let iframeArr = iframeWin.arr;
                console.log('iframeArr : ' + JSON.stringify(iframeArr));
                console.log('iframeArr instanceof Array : ' + (iframeArr instanceof Array));//
                console.log('iframeArr instanceof iframeWin.Array : ' + (iframeArr instanceof iframeWin.Array));//
            }
        </script>  
    </body>
</html>

iframe.html

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title>iframe</title>  
    </head>
    <body>
        <p>iframe</p>
        <script type="text/javascript">
            window.onload = function(){
     
     
                window.arr = [6,7,8,9,10];
            }
        </script>  
    </body>
</html>

npx http-server가 main.html을 연 후 결과를 얻습니다.

여기에 사진 설명 삽입

다른 전역 변수에서 동일한 이름을 가진 생성자는 동일한 함수가 아니라는 것을 알 수 있습니다. 데이터와 유형이 동일한 전역 변수 아래에 있지 않은지 판단하기 위해 instanceof를 사용하는 것은 불가능합니다.

Object.prototype.toString 메서드

Object.prototype.toString은 교차 전역 변수의 영향을받지 않습니다.

main.html

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title>main</title>      
    </head>
    <body>
        <iframe src="./iframe.html"></iframe>
        <script type="text/javascript">
            window.onload = function(){
     
     
                let toString = Object.prototype.toString;
                console.log('document.domain : ' + document.domain);
                let mainArr = [1,2,3,4,5];
                console.log('toString.call(mainArr) : ' + (toString.call(mainArr)));//
                let iframe = document.documentElement.getElementsByTagName('iframe')[0];
                let iframeWin = iframe.contentWindow.window;
                let iframeArr = iframeWin.arr;
                console.log('toString.call(iframeArr) : ' + (toString.call(iframeArr)));//
            }
        </script>  
    </body>
</html>

iframe.html

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title>iframe</title>  
    </head>
    <body>
        <p>iframe</p>
        <script type="text/javascript">
            window.onload = function(){
     
     
                window.arr = [6,7,8,9,10];
            }
        </script>  
    </body>
</html>

npx http-server가 main.html을 연 후 결과를 얻습니다.
여기에 사진 설명 삽입

그러나 Symbol.toStringTag를 사용하면 Object.prototype.toString의 출력에 영향을줍니다.

let toString = Object.prototype.toString;

function Person(){
    
    

}

let p = new Person();

console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Object]

Object.defineProperty(p,Symbol.toStringTag,{
    
    
    get(){
    
    
        return "Person";
    }
})

console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Person]

당신은 또한 수:

let toString = Object.prototype.toString;

function Person(){
    
    

}

let p = new Person();

console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Object]

Object.defineProperty(Person.prototype,Symbol.toStringTag,{
    
    
    get(){
    
    
        return "Person";
    }
})

console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Person]

다음과 같이 작성할 수도 있습니다.

let toString = Object.prototype.toString;

class Person{
    
    
    get [Symbol.toStringTag](){
    
    
        return 'Person';
    }
}

let p = new Person();

console.log('toString.call(p) : ' + toString.call(p));//toString.call(p) : [object Person]

Array.isArray 메서드

쓰기 가능한 속성 값이 true이므로 Array.isArray를 수정할 수 있습니다.

Object.getOwnPropertyDescriptor(Array,'isArray');
//{writable: true, enumerable: false, configurable: true, value: ƒ}

Array.isArray = function(data){
    
    
    return null !== data && typeof data === 'object';
}

console.log(Array.isArray(window));//true

Array.isArray는 교차 전역 변수의 영향을받지 않으며 Symbol.toStringTag를 수정해도 Array.isArray의 판단에 영향을주지 않습니다.

let toString = Object.prototype.toString;

Object.defineProperty(Array.prototype,Symbol.toStringTag,{
    
    
    get(){
    
    
        return "Person";
    }
})

let arr = new Array();

console.log(Array.isArray(arr));//true

console.log('toString.call(arr) : ' + toString.call(arr));//toString.call(arr) : [object Person]

특정 Array.isArray 판단 논리 array-isarray.tq 파일 에서 v8찾았습니다 .

// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

namespace runtime {
    
    
  extern runtime ArrayIsArray(implicit context: Context)(JSAny): JSAny;
}  // namespace runtime

namespace array {
    
    
  // ES #sec-array.isarray
  javascript builtin ArrayIsArray(js-implicit context:
                                      NativeContext)(arg: JSAny): JSAny {
    
    
    // 1. Return ? IsArray(arg).
    typeswitch (arg) {
    
    
      case (JSArray): {
    
    
        return True;
      }
      case (JSProxy): {
    
    
        // TODO(verwaest): Handle proxies in-place
        return runtime::ArrayIsArray(arg);
      }
      case (JSAny): {
    
    
        return False;
      }
    }
  }
}  // namespace array

종료

이 재능에 대한 지식이 부족하여 문제가 발견되면 저에게 지적하고 싶습니다. 감사합니다.

추천

출처blog.csdn.net/qq_35508835/article/details/113871051