js 基本/引用数据类型、深拷贝和浅拷贝

547 阅读3分钟

一、基本数据类型

Number( NaN 是 number 类型的特殊数值)、String、Boolean、Null、Undefind、Symbol

  1. 对于基本数据类型赋值是复制值js会创建原变量值的一个副本,并将这个副本存储到新变量的栈内存空间中)。
  2. 存放在栈内存中(存放的是变量名(例如:num)和值(例如:234))。
基本类型 传 的是值
//引用类型 传 的是地址

let a = 10; // 基本类型(number),栈内存中存储值 10
let b = a;  // 复制 a 的值(10),b 的栈内存中存储独立的 10

b = 20;     // 修改 b 的值,仅影响 b 自己的栈内存
console.log(a); // 输出 10(a 的值不受影响)
console.log(b); // 输出 20

let num = 234
 function adds(a) {
    a = 'sy'
  }
adds(num)
console.log(num);//234
基本数据类型的值不可变
let str = 'lasdjfo'
str[0] = 'o'
console.log(str);//'lasdjfo'

二、引用(复杂)数据类型

Object 是引用数据类型的总称;Array、RegExp、Date、Math、Function 属于引用数据类型

  • 对于引用数据类型赋值则是复制地址(两个变量都指向同一个内存地址),所以会改变原始值。
  • 对于赋值和深、浅拷贝发生的数据变化都只针对 引用类型
  • 存放在堆内存中(栈中存放的是变量名和内存地址)。
  • 引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。
引用类型 传 的是地址
let obj = { name: 'doisa' }
 function adds(a) {
    a.name = 'sy'
  }
adds(obj)
console.log(obj);//{name:'sy'}

image.png

三、深浅拷贝

let obj = {name:'sy',arr:[1,2,3,4]}
1、浅拷贝和赋值

复制的对象值会关联到原数据。

  1. 浅拷贝:只复制一层属性(name),不会复制对象里的引用数据类型(arr)
  2. 赋值:赋的是地址,一层属性和对象里的引用数据类型都会改变
2、深拷贝

复制每一个子项,直到子项为基础类型。

1、赋值对象的所有属性及对象里的引用数据类型进行赋值,放在一个新的堆中

var student = {
              "name": "sy",
              "age": 17,
              "study": function () {
                console.log("learn web");
              },
              "major": ["WEB", "CSS", "JS"],
              "address": { "now": "成都", "par": "雅安" }
        };
//深拷贝方法
        function deepCopy(source) {

            // source如果是对象或数组,且不为null时遍历复制子元素,否则直接返回。 

            // typeof(数组)、typeof(对象)、typeof(null) 返回的都是 "object"

            if (typeof (source) === "object" && source !== null) {

                //判断要复制的对象是数组还是对象。
                let target = source instanceof Array ? [] : {}; //方法1、source是否Array的实例

                //let target = source.constructor === Array ? [] : {}; //方法2、返回创建此对象的函数引用

                //let target = Object.prototype.toString.call(source) === '[object Array]' ? [] : {}; //方法3、

                //let target = Array.isArray(source) ? [] : {}; //方法4、ES5

                //遍历source => for in 是 对象和数组都可以使用的语法
                for (var key in source) {

                    //判断是否自身属性
                    if (source.hasOwnProperty(key)) {

                        //子元素如果是对象或数组,且不为null需要继续递归复制
                        if (typeof source[key] === 'object' && source !== null) {

                            target[key] = deepCopy(source[key]);

                        } else {

                            target[key] = source[key];
                        }
                    }
                }

                return target;

            } else {

                return source;

            }

        }

四、数据类型判断

1、typeof 方法

只能判断基本数据类型,null 不能判断,Function 可以判断;返回结果首字母小写

 console.log(typeof 1);//number
 console.log(typeof 'a');//string
 console.log(typeof true);//boolean
 console.log(typeof undefined);//undefind
 console.log(typeof Symbol('sy'));//symbol
 console.log(typeof function () { });//function
 console.log(typeof null);//null 不能判断 结果为:object
2、instanceof 方法

只能判断引用数据类型;返回 true 和 false

console.log([] instanceof Array);//true
console.log(new Array() instanceof Array);//true
console.log(new RegExp instanceof RegExp);//true
console.log(new String('sdf') instanceof String);//true
console.log({} instanceof Object);//true
console.log('sdfgj' instanceof String);//false 只能判断复杂数据类型
3、Object.prototype.toString 方法

需要加 call() 方法使用;输出结果为 [object Xxx] 首字母大写

console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call({}));//[object Object]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call('ao\'duf'));//[object String]
console.log(Object.prototype.toString.call(Symbol('dgfh')));//[object Symbol]

问:

1、数据什么时候可以放在栈上,什么时候需要放在堆上呢?

栈:固定大小的数据可以放在栈上;当前作用域的数据可以放在栈上。
堆:大小变化的数据放堆上;全局数据。