数据类型
根据变量类型传递方式,可以分为基本数据类型和引用数据类型
- 基本数据类型:Number、Boolean、String、Undefined、Null、Symbol(es6)
- Null表示空对象指针
- Undefined表示未定义
- 引用类型:Object是所有引用类型的父对象
- Object、Function、Arguments、Date、Math、Error、RegExp、自定义对象等
传参方式
- 基本数据类型和引用类型都是按值传递
- 按值传递,传递的是变量里的内容,不管盒子里面存的是基本类型值还是引用类型的地址
//不影响
var obj = {
value: 1
};
function foo(o) {
o = 2;
console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1
//影响
var obj = {
value: 1
};
function foo(o) {
o.value = 2;
console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2
存储空间
基本数据类型存储在内存栈中,引用类型的数据存储在堆中(引用存储在内存栈)
image
类型检测
typeof
- 不要用typeof做类型检测
- typeof null会错误的返回Object
- 在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"
- typeof Function会返回function
- typeof 无法区别基本数据类型和引用类型
// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';
类型检测的通用方法
使用Object.prototype.toString()可以获取对象的[[Class]]内部属性。[[Class]]内部属性根据标准,只可能是下面字符串中的一个:
Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String.
function isType(obj,type){
//[object xxx]
let classProperty = Object.prototype.toString.call(obj).slice(8,-1)
return classProperty === type
}
//true
is(new String("a"),"String")
//true
is("a","String")
instanceof
object instanceof constructor用来检测一个对象是否为某个构造函数的实例
function A() {
}
function B() {
}
B.prototype = new A()
let b = new B()
b instanceof B // true
b instanceof A // true
instanceof原理:
//判断当前构造函数的prototype是否在原型链上
function new_instance_of(leftVaule, rightVaule) {
let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
while (true) {
if (leftVaule === null) {
return false;
}
if (leftVaule === rightProto) {
return true;
}
leftVaule = leftVaule.__proto__
}
}
加深原型链理解:
image
- 可以知道prototype是构造函数独有属性,proto是任意对象都有的(不管当前对象是作为构造函数还是原型,)并且指向其构造函数的prototype,constructor的是原型独有的属性
- Function对象是任意构造函数的构造函数(包括function Object),所以任意构造函数的proto指向Function.prototype
- 任意对象最终继承自Object.prototype(最终proto指向Object.prototype),Object.prototype.proto按规定是null
- Function的prototype和proto都指向Function.prototype
function Foo() {
}
//因为Object作为函数则Object.__proto__等于Function.prototype,而Function.prototype是对象,所以Function.prototype.__proto__为Object.prototype
Object instanceof Object // true
Function instanceof Function // true
Function instanceof Object // true
Foo instanceof Foo // false
Foo instanceof Object // true
Foo instanceof Function // true
赋值、浅拷贝和深拷贝
image赋值和浅拷贝的区别
- 如果将一个对象赋值给新变量,赋的是对象在栈中的地址,不是堆中的数据。也就是新的变量和赋值的对象指向同一个存储空间。
- 浅拷贝是按位拷贝,会创建一个新的对象,对原始对象的属性进行拷贝,如果属性是基本类型,则拷贝的是基本类型的值,如果属性是引用类型,则拷贝的是引用类型的内存地址。
//对象赋值
var obj = {
name:'johe',
age:'18',
arr:[1,2]
}
var obj2 = obj
obj2.name = 'johe-test'
//受影响
obj.name
//浅拷贝
var obj = {
name:'johe',
age:'18',
arr:[1,2]
}
function shallowCopy(obj){
var cloneObj = {}
for(let property in obj){
// for in 会访问原型链上的属性,所以需要加判断
if(obj.hasOwnProperty(property)){
cloneObj[property] = obj[property]
}
}
return cloneObj
}
var obj2 = shallowCopy(obj)
obj2.name="johe-test"
//不受影响,输出johe
obj.name
obj2.arr[1] = 3
//受影响,输出[1,3]
obj.arr
浅拷贝实现方式
- Object.assign:把任意多个源对象的可枚举属性拷贝给目标对象,然后返回目标对象。当被拷贝对象只有一层(一层全是基本数据类型)时,是深拷贝
var obj = {
arr:[1,2]
}
var cloneObj = Object.assign({},obj)
cloneObj.arr = [1,3]
//受影响,输出[1,3]
obj.arr
- Array.prototype.concat()
let arr = [1,2,{name:'johe'}]
let arr2 = arr.concat[]
arr2[2].name = "johe-test"
//受影响输出johe-test
arr[2].name
- Array.prototype.slice()
浅拷贝和深拷贝的区别
image浅拷贝和深拷贝都会返回一个新对象,浅拷贝在拷贝对象属性时,如果是基本数据类型,则拷贝值,如果是引用类型,则拷贝引用(指向同一地址)。
深拷贝在拷贝对象属性时,如果是基本数据类型,则拷贝值,如果是引用类型,则生成新对象递归拷贝。
深拷贝实现
- JSON.parse(JSON.stringify()):最常用的深拷贝方法,但不能处理函数和正则
- 手写递归方法:
function isType(type,obj){
return Object.prototype.toString.call(obj).slice(8,-1) === type
}
function deepClone(target){
let result
if(isType('Object',target)){
result = {}
}else if(isType('Array',target)){
result = []
}else{
return target
}
for(let i in target){
let value = target[i]
result[i] = deepClone(value)
}
return result
}
网友评论