自学内容网 自学内容网

js 数据类型=》理解=》应用

js 类型判断

  1. JavaScript数据类型概述
    • JavaScript中有两种数据类型:基本数据类型(也叫基础类型)和复合数据类型(也叫引用类型)。基本数据类型的值是不可变的,而复合数据类型的值是可变的,因为它们存储的是引用地址,通过引用可以修改对象的内容。
  2. 基础类型及判断方法
    • Number(数字)类型
      • 包括整数和浮点数,例如13.14等。
      • 判断方法:
        • 使用typeof操作符,typeof 1返回'number'typeof 3.14也返回'number'。但是typeof NaN也返回'number',需要注意NaN是一个特殊的数字值,表示“不是一个数字”,但它的数据类型仍然是Number
    • String(字符串)类型
      • 由零个或多个字符组成,用单引号或双引号括起来,如'hello'"world"
      • 判断方法:
        • typeof 'hello'返回'string'typeof "world"也返回'string'
    • Boolean(布尔)类型
      • 只有两个值truefalse,用于逻辑判断。
      • 判断方法:
        • typeof true返回'boolean'typeof false返回'boolean'
    • null类型
      • 表示一个空值,它是一个特殊的关键字,只有一个值null
      • 判断方法:
        • typeof null返回'object'(这是JavaScript中的一个历史遗留问题),要准确判断是否为null可以使用value === nullvalue为要判断的变量)。
    • undefined类型
      • 当一个变量声明但未赋值时,它的值是undefined
      • 判断方法:
        • typeof undefined返回'undefined'
  3. 复合类型及判断方法
    • Object(对象)类型
      • 包括普通对象({})、数组([])、函数(function(){})等。
      • 判断方法:
        • 对于普通对象,可以使用typeof操作符,typeof {}返回'object'。但是对于数组和函数,typeof操作符不能准确判断。对于数组,Array.isArray([])返回true,可以准确判断是否为数组。对于函数,function isFunction(value) { return typeof value === 'function'; },使用这样的自定义函数可以判断是否为函数。例如isFunction(function(){})返回true
    • Array(数组)类型
      • 是一种特殊的对象,用于存储多个值,如[1, 2, 3]
      • 判断方法:
        • 除了上面提到的Array.isArray方法外,还可以通过Object.prototype.toString.call([1,2,3])返回'[object Array]'来判断。这种方法可以在一些不支持Array.isArray的旧环境中使用。
    • Function(函数)类型
      • 函数是一种特殊的对象,可以被调用执行一段代码,如function add(a,b) { return a + b; }
      • 判断方法:
        • 除了自定义isFunction函数外,还可以通过Object.prototype.toString.call(add)返回'[object Function]'来判断。

在JavaScript中,准确判断数据类型对于编写健壮的代码非常重要,特别是在处理不同类型的数据进行不同操作的场景下,要根据具体情况选择合适的判断方法。

Object.prototype.toString.call 方法的理解与实现

一、对Object.prototype.toString.call方法的理解

  1. 本质

    • Object.prototype.toString.call()是JavaScript中用于获取对象内部[[Class]]属性值(在ES6规范之前,类似于对象的类型标识,现在更准确地说是对象的规范类型标签)的一种方法。它返回一个形如"[object <类型名称>]"的字符串,其中<类型名称>就是对象的具体类型标识。
  2. 用途

    • 准确判断数据类型:在JavaScript中,typeof操作符虽然可以判断一些基本数据类型,但对于像数组、正则表达式等特殊对象类型的判断并不准确。例如,typeof []返回"object",而Object.prototype.toString.call([])则能准确返回"[object Array]",从而清晰地表明该对象是数组类型。同样,对于函数、日期对象等,它也能准确给出其类型标识,如Object.prototype.toString.call(function() {})返回"[object Function]"Object.prototype.toString.call(new Date())返回"[object Date]"等。
    • 区分不同类型的对象:当需要针对不同类型的对象执行不同的操作时,通过该方法可以精确地确定对象类型,进而编写更具针对性和健壮性的代码。比如在一个处理多种数据类型的函数中,先判断传入数据的准确类型,再根据类型进行相应的处理。

二、Object.prototype.toString.call方法的实现原理

  1. toString方法的原生行为

    • 在JavaScript中,每个对象都继承自Object.prototype,而Object.prototype上定义了toString方法。当直接在一个对象上调用toString方法(如myObject.toString())时,其默认行为是返回一个字符串,这个字符串通常是"[object Object]",除非该对象自己重写了toString方法来返回其他有意义的值。
  2. call方法的作用

    • call是函数的一个方法,它允许我们改变函数内部的this指向,并立即执行该函数。当我们使用Object.prototype.toString.call(obj)时,实际上是将obj作为this传递给了Object.prototype.toString函数。这样做的目的是为了让toString函数在判断类型时,是基于我们传入的具体对象obj来获取其内部的[[Class]]属性值,而不是基于Object.prototype自身(因为直接调用Object.prototype.toString()会返回"[object Object]",这不是我们想要的针对具体对象的类型判断结果)。

三、简单的模拟实现示例

以下是一个简单的模拟实现Object.prototype.toString.call方法功能的示例代码:

function customToStringCall(obj) {
    var toString = Object.prototype.toString;
    return toString.call(obj);
}

// 测试示例
var arr = [1, 2, 3];
var func = function() {};
var date = new Date();

console.log(customToStringCall(arr)); // "[object Array]"
console.log(customToStringCall(func)); // "[object Function]"
console.log(customToStringCall(date)); // "[object Date]"

在这个示例中,我们定义了一个函数customToStringCall,它接受一个对象作为参数。在函数内部,我们先获取了Object.prototype上的toString方法,然后通过call方法将传入的对象作为this传递给toString方法,从而实现了类似Object.prototype.toString.call的功能,能够准确判断对象的类型并返回相应的类型标识字符串。

需要注意的是,这只是一个简单的模拟实现,实际的JavaScript引擎内部对于Object.prototype.toString.call的实现要复杂得多,涉及到更多关于对象模型、类型系统等方面的底层机制。但通过这个模拟实现,可以帮助我们更好地理解其工作原理。

Object.prototype上的toString方法 理解应用

以下是一个简单的示例来展示Object.prototype上的toString方法的基本使用情况以及通过模拟它的部分行为来加深理解:

// 定义一个简单对象
let myObject = {
    name: 'John',
    age: 30
};

// 直接在对象上调用toString方法(这种情况下会返回默认格式的字符串)
let defaultToStringResult = myObject.toString();
console.log(defaultToStringResult); 
// 输出: [object Object],因为没有重写toString方法,所以返回默认的表示形式

// 现在来模拟一下Object.prototype.toString方法的部分行为

// 定义一个函数来模拟获取对象类型标识的功能
function customToString(obj) {
    if (obj === null) {
        return "[object Null]";
    } else if (obj === undefined) {
        return "[object Undefined]";
    } else {
        let type = typeof obj;
        if (type === 'object') {
            if (Array.isArray(obj)) {
                return "[object Array]";
            } else if (obj instanceof Date) {
                return "[object Date]";
            } else if (obj instanceof Function) {
                return "[object Function]";
            } else {
                return "[object Object]";
            }
        } else {
            return `[object ${type}]`;
        }
    }
}

// 测试自定义的toString函数
console.log(customToString(myObject)); 
// 输出: [object Object],和直接调用myObject.toString()结果相同

let myArray = [1, 2, 3];
console.log(customToString(myArray)); 
// 输出: [object Array]

let myFunction = function() {};
console.log(customToString(myFunction)); 
// 输出: [object Function]

let myDate = new Date();
console.log(customToString(myDate)); 
// 输出: [object Date]

let myNullValue = null;
console.log(customToString(myNullValue)); 
// 输出: [object Null]

let myUndefinedValue = undefined;
console.log(customToString(myUndefinedValue)); 
// 输出: [object Undefined]

在上述代码中:

  • 首先创建了一个普通对象myObject,并直接调用了它的toString方法,得到了默认的[object Object]结果。
  • 然后定义了一个customToString函数来模拟Object.prototype.toString方法获取对象类型标识的功能。这个函数通过判断对象的不同情况(是否为nullundefined,以及如果是object类型再进一步判断是数组、日期、函数等具体类型)来返回相应的[object <类型>]格式的字符串。
  • 最后对不同类型的对象(普通对象、数组、函数、日期、nullundefined)分别用自定义函数进行了测试,展示了不同情况下的输出结果,类似于Object.prototype.toString方法在不同对象上的表现。

不过需要注意的是,这只是一个非常简化的模拟,实际的Object.prototype.toString方法在JavaScript引擎内部的实现要复杂得多,涉及到更多关于对象模型、类型系统等方面的底层机制。

补充(symbol/BigInt)

  1. Symbol类型
    • 基本概念:Symbol是ES6引入的一种新的原始数据类型,它的值是唯一的,用于创建对象的私有属性。Symbol值通过Symbol()函数生成,每次调用Symbol()都会返回一个新的、独一无二的Symbol值。
    • 应用场景和实例
      • 对象属性名的唯一性
        • 在JavaScript中,对象的属性名通常是字符串。但有时候我们希望某些属性是独一无二的,这时候就可以使用Symbol。例如,我们有一个对象来存储用户的信息,其中包括一些公共属性和一些内部使用的属性。
        let user = {
            name: 'John',
            age: 30
        };
        let internalProperty = Symbol('internal');
        user[internalProperty] = 'This is an internal value';
        console.log(user);
        // 输出: {name: "John", age: 30, Symbol(internal): "This is an internal value"}
        
        • 在这个例子中,internalProperty是一个Symbol值,它被用作user对象的属性名。这样可以确保这个属性不会与其他可能的属性名冲突,因为Symbol值是唯一的。
      • 实现类的私有属性(在JavaScript的模拟意义上)
        • 虽然JavaScript没有像传统面向对象语言那样严格的私有属性概念,但可以使用Symbol来模拟。例如,我们有一个简单的类(使用函数和原型来模拟类)来表示一个计数器。
        let counterSymbol = Symbol('counter');
        function Counter() {
            this[counterSymbol] = 0;
        }
        Counter.prototype.increment = function() {
            this[counterSymbol]++;
        };
        Counter.prototype.getValue = function() {
            return this[counterSymbol];
        };
        let myCounter = new Counter();
        myCounter.increment();
        console.log(myCounter.getValue()); 
        // 输出: 1
        
        • 在这个Counter类中,counterSymbol是一个Symbol值,用来存储计数器的当前值。这个值在外部无法直接访问,只能通过getValue方法来获取,从而实现了一种模拟的私有属性效果。
      • 避免命名冲突的全局常量池
        • 在一个大型的JavaScript应用中,可能会有多个模块或库使用相同的属性名。为了避免冲突,可以使用Symbol来创建全局常量池。例如,假设有两个模块,一个是用户认证模块,一个是用户权限模块。
        // 用户认证模块
        let AUTH_SYMBOL = Symbol('auth');
        let userAuth = {
            [AUTH_SYMBOL]: {
                token: 'abc123',
                expires: '2024-12-31'
            }
        };
        // 用户权限模块
        let PERMISSION_SYMBOL = Symbol('permission');
        let userPermission = {
            [PERMISSION_SYMBOL]: ['read', 'write']
        };
        
        • 这里使用Symbol来定义每个模块的关键属性,这样可以确保即使两个模块的属性在概念上可能不同,但不会因为使用了相同的字符串属性名而产生冲突。
  2. BigInt类型
    • 基本概念:BigInt是ES11引入的数据类型,用于表示任意精度的整数。在JavaScript中,Number类型有一定的范围限制,对于非常大的整数会出现精度问题。BigInt可以处理这些超大型整数。
    • 应用场景和实例
      • 处理超大整数运算
        • 在密码学、金融计算等领域,经常需要处理非常大的整数。例如,在计算阶乘时,数字可能会迅速增长到超出Number类型的范围。
        function factorial(n) {
            let result = BigInt(1);
            for (let i = BigInt(2); i <= BigInt(n); i++) {
                result *= i;
            }
            return result;
        }
        console.log(factorial(100));
        // 输出一个非常大的整数,用BigInt表示,不会出现精度问题
        
        • 这个例子中,通过使用BigInt类型,我们可以准确地计算出100的阶乘,而不会像使用Number类型那样因为精度问题得到错误的结果。
      • 数据库中的大整数存储和运算(与后端交互)
        • 当JavaScript应用与后端数据库交互时,数据库中可能存储了非常大的整数。例如,在一个存储全球用户编号的系统中,用户编号可能是一个巨大的整数。
        // 假设从数据库获取了一个大整数的用户编号
        let userIDFromDB = BigInt('123456789012345678901234567890');
        console.log(userIDFromDB);
        // 可以对这个大整数进行各种运算,如加、减、乘、除等操作
        let newID = userIDFromDB + BigInt(1);
        console.log(newID);
        
        • 这里展示了如何从数据库获取一个大整数(以字符串形式存储,因为JavaScript的Number类型无法直接处理这么大的数字),并将其转换为BigInt类型,然后进行简单的加法运算。
      • 高精度计算场景
        • 在一些科学计算或算法竞赛中,可能需要进行高精度的整数计算。例如,计算两个非常大的质数相乘。
        let prime1 = BigInt('99999999999999999999');
        let prime2 = BigInt('88888888888888888888');
        let product = prime1 * prime2;
        console.log(product);
        
        • 这个例子展示了使用BigInt类型来计算两个超大质数的乘积,确保计算结果的准确性。

NaN or Number.isNaN

  1. isNaN函数介绍
    • 基本定义isNaN()是一个全局函数,用于判断一个值是否为NaN(Not - a - Number)。它的参数会被强制转换为数字类型,然后判断转换后的结果是否为NaN
    • 应用场景和原理
      • 当从用户输入或者其他数据源获取到一个可能是数字的值时,需要判断这个值是否为有效数字。例如,在一个表单验证的函数中,检查用户输入的是否是一个有效的数字。
      function validateNumber(input) {
          return!isNaN(input);
      }
      console.log(validateNumber('123')); // true
      console.log(validateNumber('abc')); // false
      console.log(validateNumber(NaN)); // false
      
      • 在这个例子中,isNaN函数会尝试将输入的input值转换为数字。如果转换成功且不是NaN,则返回false,函数validateNumber返回true,表示是一个有效数字;如果转换后是NaN,则返回truevalidateNumber返回false
      • 它也可以用于检查数学运算的结果是否为NaN。例如,在进行除法运算时,当除数为0时,结果是NaN
      function divide(a, b) {
          let result = a / b;
          return!isNaN(result);
      }
      console.log(divide(10, 2)); // true
      console.log(divide(5, 0)); // false
      
  2. Number.isNaN函数介绍
    • 基本定义Number.isNaN()是ES6新增的方法,它与isNaN不同,不会对传入的参数进行类型转换,直接判断参数是否为NaN
    • 应用场景和原理
      • 在需要精确判断一个值是否为NaN,而不希望有任何类型转换干扰的场景下使用。例如,在一个处理数字数组的函数中,要判断数组中的某个元素是否为NaN
      function checkArrayForNaN(arr) {
          for (let i = 0; i < arr.length; i++) {
              if (Number.isNaN(arr[i])) {
                  return true;
              }
          }
          return false;
      }
      let numberArray = [1, 2, NaN];
      console.log(checkArrayForNaN(numberArray)); // true
      let stringArray = ['a', 'b', 'c'];
      console.log(checkArrayForNaN(stringArray)); // false
      
      • 在这里,Number.isNaN直接判断数组元素是否为NaN,不会对字符串等非数字类型进行转换后再判断,所以对于stringArray,它不会产生误判。
  3. 两者的区别
    • 类型转换方面
      • isNaN会对传入的参数进行类型转换,将其转换为数字类型后再判断是否为NaN。例如,isNaN('abc')会先尝试将'abc'转换为数字(转换结果为NaN),然后返回true
      • Number.isNaN不会进行类型转换,直接判断传入的参数是否是NaN。所以Number.isNaN('abc')返回false,因为'abc'不是NaN
    • 应用场景方面
      • isNaN在一些不太严格的场景下使用,比如简单的用户输入验证,只要最终转换后不是NaN就可以认为是有效的数字相关内容。
      • Number.isNaN用于更精确的判断,尤其是在处理已经确定是数字类型或者需要严格判断是否为NaN的场景,如在数学计算后的结果检查或者处理纯数字数据结构中的元素判断。

JSON

  1. JSON的基本概念
    • JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它基于JavaScript的语法,但可以被多种编程语言使用,用于在不同系统之间传递数据。JSON数据格式简洁、易于阅读和编写,并且易于机器解析和生成。
    • 它主要由两种结构组成:对象(Object)和数组(Array),并且可以包含基本数据类型,如字符串(String)、数字(Number)、布尔值(Boolean)和特殊值(null)。
  2. JSON对象的结构和特点
    • 对象结构
      • JSON对象是一个无序的“键 - 值”对集合,用花括号{}包裹。键是一个字符串,必须用双引号""括起来,值可以是任意合法的JSON数据类型,包括其他对象、数组、基本数据类型等。例如:
      {
          "name": "John",
          "age": 30,
          "city": "New York",
          "hobbies": ["reading", "traveling"],
          "address": {
              "street": "123 Main St",
              "zip": "10001"
          }
      }
      
      • 在这个例子中,nameagecity等是对象的键,它们对应的"John"30"New York"等是值。hobbies是一个包含字符串的数组,address是一个嵌套的对象。
    • 特点
      • 自我描述性:JSON对象的结构使得数据具有自我描述性。从键的名称可以大致了解对应值的含义。例如,通过"age": 30,很容易理解这个数据是关于某个人的年龄信息。
      • 层次结构:可以通过嵌套对象和数组构建复杂的层次结构。如上述例子中的address对象嵌套在主对象中,用于存储更详细的地址信息;hobbies数组用于存储多个兴趣爱好。
  3. JSON数组的结构和特点
    • 数组结构
      • JSON数组是一个有序的值的集合,用方括号[]包裹,数组中的元素可以是任意合法的JSON数据类型,包括其他数组、对象、基本数据类型等。例如:
      [
          {
              "id": 1,
              "product": "Book",
              "price": 19.99
          },
          {
              "id": 2,
              "product": "Pen",
              "price": 1.99
          }
      ]
      
      • 这里是一个包含两个对象的数组,每个对象代表一个商品的信息,包括商品的idproduct名称和price
    • 特点
      • 有序性:与对象不同,数组中的元素是有序的。这使得它适用于表示按特定顺序排列的数据,如列表、序列等。在上面的商品列表例子中,可以通过索引来访问每个商品,如array[0]表示第一个商品对象,array[1]表示第二个商品对象。
      • 可扩展性:可以轻松地添加或删除元素来更新数据。例如,要添加一个新商品,只需将新的商品对象插入到数组中合适的位置即可。
  4. JSON的应用场景
    • 网络数据传输
      • JSON是网络应用中最常用的数据传输格式之一。在前后端分离的架构中,后端服务器将数据以JSON格式发送给前端浏览器。例如,一个电商网站的后端API返回商品列表、用户订单等信息时,通常使用JSON格式。这样前端JavaScript代码可以轻松地解析这些数据并展示给用户。
    • 配置文件
      • 许多软件和应用使用JSON作为配置文件格式。它比传统的INI文件或XML文件更简洁,易于理解和编辑。例如,一个Node.js应用的配置文件可能如下所示:
      {
          "port": 3000,
          "database": {
              "host": "localhost",
              "user": "admin",
              "password": "123456",
              "database": "mydb"
          },
          "logging": {
              "level": "info"
          }
      }
      
      • 这个配置文件定义了应用运行的端口、数据库连接信息和日志记录级别等重要参数。
    • 数据存储(与数据库交互)
      • 一些数据库支持直接存储和读取JSON格式的数据。例如,MongoDB是一种流行的NoSQL数据库,它以BSON(Binary JSON)格式存储数据,与JSON非常相似。在这种数据库中,可以将复杂的数据结构,如包含嵌套对象和数组的用户文档,直接存储为JSON - like的数据格式,方便数据的存储和查询。

原文地址:https://blog.csdn.net/m0_51244077/article/details/143696167

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!