前言
引用数据类型主要用于区别基本数据类型,描述的是具有属性和函数的对象。
JavaScript中常用的引用数据类型包括**Object类型**
、**Array类型**
、**Date类型**
、**RegExp类型**
、**Math类型**
、**Function类型**
以及**基本数据类型的包装类型**
,如Number类型、String类型、Boolean类型等。
- 引用数据类型的实例需要通过new操作符生成,有的是显式调用,有的是隐式调用。
- 引用数据类型变量赋值传递的是内存地址。
- 引用数据类型的比较是对内存地址的比较,而基本数据类型的比较是对值的比较。
new 操作符
new操作符在执行过程中会改变this的指向,所以在了解new操作符之前,我们先解释一下this的用法。
this的用法:
事实上我们并未通过return返回任何值,为什么输出的信息中会包含name和age属性呢?其中起作用的就是this这个关键字了。在JavaScript中,如果函数没有return值,则默认return this。代码里his实际值是Cat空对象,后两句就相当于给Cat对象添加name和age属性。function Cat(name, age){
console.log(this) // Cat {}
this.name = name;
this.age = age;
}
console.log(new Cat('miaomiao', 8)) // Cat {name: "miaomiao", age: 8}
new操作符背后做了什么: ```javascript var cat = new Cat()
// 实际new操作符做了3件事 var cat = {}; cat.proto = Cat.prototype; Cat.call(cat);
1. 创建一个空对象。
1. 将空对象的__proto__属性指向Cat对象的prototype属性。
1. 将Cat()函数中的this指向cat变量。于是cat变量就是Cat对象的一个实例。
<a name="jP6cj"></a>
# Object类型
<a name="miARd"></a>
## Object类型的实例函数
<a name="kFVSa"></a>
### 1. hasOwnProperty(propertyName)函数
该函数的作用是判断对象自身是否拥有指定属性,不会检查实例对象原型链上的属性。
```javascript
// 1. Object
var o = new Object();
o.name = 'xjp'
console.log(o.hasOwnProperty('name')) // true name属性为实例o自己定义的,而非继
console.log(o.hasOwnProperty('toString')) // false toString为继承属性
// 2. 自定义对象
var Student = function(name){
this.name = name
}
Student.prototype.sayHi = function(){
alert('Hi' + this.name)
}
var st = new Student('张三')
console.log(st.hasOwnProperty('name')) // true 调用构造函数时,通过this.name附加到实例对象上
console.log(st.hasOwnProperty('sayHi')) // false sayHi为原型上的成员
2. propertyIsEnumerable(propertyName)函数
该函数的作用是判断指定属性是否为实例属性并且是否是可枚举的,如果是原型链上的属性或者不可枚举都将返回“false”。
// 1. 数组
var arr = [1,2,3];
arr.name = 'Array'
console.log(arr.propertyIsEnumerable('name')) // true name为实例属性
console.log(arr.propertyIsEnumerable('join')) // false join函数继承自Array类型
console.log(arr.propertyIsEnumerable('length')) // false length属性继承自Array类型
console.log(arr.propertyIsEnumerable('toString')) // false toString函数继承自Object类型
// 2. 自定义对象
var Student = function(name){
this.name = name
}
Student.prototype.sayHi = function(){
alert('Hi' + this.name)
}
var a = new Student('tom')
console.log(a.propertyIsEnumerable('name')) // true name为自身定义的实例属性
console.log(a.propertyIsEnumerable('sayHi')) // // false sayHi为原型上的成员
Object.defineProperty(a, 'name', {
enumerable:false
})
console.log(a.propertyIsEnumerable('name')); // false name设置不可枚举
Object类型的静态函数
静态函数指的是方法的调用基于Object类型自身,不需要通过Object类型的实例。
1. Object.create(prototype, prototypeDescriptor)函数
作用:创建并返回一个指定原型和指定属性的对象。该对象的proto === 指定原型对象。
第一个参数prototype为对象的原型,可以为null。若为null,则对象的原型为undefined
第二个参数prototypeDescriptor属性描述符,格式如下:
{
value: '', // 设置属性的值
writable: true, // 设置此属性是否可写入修改;默认false:只读
enumerable: true, // 设置此属性是否可枚举;默认false:不可以枚举
configurable: true // 设置此属性是否可配置,如是否可以修改此属性的特性及是否可以删除属性;默认fakse
}
2. Object.defineProperties(obj, prototypeDescriptor)函数
3. Object.getOwnPropertyNames(obj)函数
作用:获取对象的所有实例属性和函数,不包含原型链继承的属性和函数,数据格式为数组。
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.getName = function(){
return this.name
}
}
Person.prototype.eat = function(){
return '吃饭'
}
var p = new Person();
console.log(Object.getOwnPropertyNames(p)) // ['name', 'age', 'gender', 'getName'] eat()函数处在原型上
4. Object.keys()函数
作用:获取对象可枚举的实例属性,不包含原型链继承的属性,数据格式为数组。
keys()函数区别于getOwnPropertyNames()函数的地方在于,keys()函数只获取可枚举类型的属性。
Array类型
判断一个变量是数组还是对象
**typeof**
运算符在判断基本数据类型时会很有用,但是在判断引用数据类型时,不能区分是数组还是对象
1. instanceof运算符
**instanceof**
运算符通过查找原型链来检测某个变量是否为某个类型数据的实例,使用**instanceof**
运算符可以判断一个变量是数组还是对象,要封装一下先判断数组类型,再判断对象类型。
// 数组不仅是Array类型的实例,也是Object类型的实例。
var arr = [1,2,3]
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true
var obj = { name: 'xjp' }
console.log(obj instanceof Array) // false
console.log(obj instanceof Object) // true
// 封装 判断变量是数组还是对象
function getDataType(o) {
if (o instanceof Array) {
return 'Array'
} else if (o instanceof Object) {
return 'Object'
} else {
return '参数不是objec类型'
}
}
2. 判断构造函数
判断一个变量是否是数组或者对象,从另一个层面讲,就是判断变量的构造函数是Array类型还是Object类型。
因为一个对象的实例都是通过构造函数生成的,所以,我们可以直接判断一个变量的constructor属性。
var arr = [1,2,3]
console.log(arr.constructor === Array) // true
console.log(arr.constructor === Object) // false
var obj = { name: 'xjp' }
console.log(obj.constructor === Array) // false
console.log(obj.constructor === Object) // true
每个变量都会有一个proto属性,表示的是隐式原型。一个对象的隐式原型指向的是构造该对象的构造函数的原型:
[].constructor === [].__proto__.constructor // true
[].__proto__ === [].constructor.prototype // true
[].__proto__ === Array.prototype // true
var arr = [1,2,3]
console.log(arr.__proto__.constructor === Array) // true
console.log(arr.__proto__.constructor === Object) // false
// 判断变量是数组还是对象
function getDataType(o) {
// 获取构造函数
var constructor = o.constructor || o.__proto__.constructor
if (constructor === Array) {
return 'Array'
} else if (constructor === Object) {
return 'Object'
} else {
return 'param is not object type'
}
}
3. toString()函数
基本任意类型都包含**toString()**
函数(除null
、undefined
)。不同数据类型的toString()函数返回值也不一样,所以通过toString()函数就可以判断一个变量是数组还是对象。
这里我们会借助**call()**
函数,直接调用Object原型上的toString()函数,把主体设置为需要传入的变量,然后通过返回值进行判断。
var a = [1, 2, 3];
var b = {name: 'kingx'};
var c;
console.log(Object.prototype.toString.call(a)); // [object Array]
console.log(Object.prototype.toString.call(b)); // [object Object]
console.log(Object.prototype.toString.call(c)); // [object Undefined]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call('xjp')); // [object String]
console.log(Object.prototype.toString.call(18)); // [object Number]
4. Array.isArray()函数
使用Array.isArray()函数只能判断出变量是否为数组,并不能确定是否为对象。
// 下面的函数调用都返回“true”
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
// 鲜为人知的事实:其实 Array.prototype 也是一个数组。
Array.isArray(Array.prototype);
// 下面的函数调用都返回“false”
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
reduce()函数
reduce()函数最主要的作用是做累加处理,即接收一个函数作为累加器,将数组中的每一个元素从左到右依次执行累加器,返回最终的处理结果。
语法:**arr.reduce(callback(accumulator、currentValue、currentIndex、array), initialValue)**
**initialValue**
用作callback的第一个参数值,如果没有设置,则会使用数组的第一个元素值。**accumulator**
表示上一次调用累加器的返回值,或设置的initialValue值。如果设置了initialValue,则accumulator=initialValue;否则accumulator=数组的第一个元素值。**currentValue**
表示数组正在处理的值。**currentIndex**
表示当前正在处理值的索引。如果设置了initialValue,则currentIndex从0开始,否则从1开始。**array**
表示数组本身。
应用场景:
- 找出数组中出现次数最多的元素
```javascript
var getMost = function(arr) {
var maxNum=1, maxEle;
var obj = arr.reduce(function(p, k) {
}, {}); // 将initialValue设置为一个空对象{},key表示数组元素,value表示元素出现的次数 return ‘出现次数最多的元素为:’+ maxEle + ‘,次数为’ + obj[maxEle] }; getMost([3, 5, 10, 10, 5, 7, 7, 10, 10]); // “出现次数最多的元素为:10,次数为4”p[k]?p[k]++ :p[k] = 1; // 这边可以统计出每个元素出现的次数
if(p[k] > maxNum) {
maxEle=k;
maxNum++;
}
return p;
// 借助ES6与逗号运算符进行代码优化 Array.prototype.getMost = function () { var obj = this.reduce((p, n) => (p[n]++ || (p[n] = 1), (p.max = p.max >= p[n] ? p.max : p[n]), (p.key = p.max > p[n] ? p.key : n), p), {}) return ‘出现次数最多的元素为:’ + obj.key + ‘,次数为:’ + obj.max } var arr = [3, 5, 10, 10, 5, 7, 7, 10, 10]; console.log(arr.getMost()); // 出现次数最多的元素为:10,次数为:4
2. 求数组的最大值和最小值
```javascript
// 最大值
Array.prototype.max = function () {
return this.reduce(function (preValue, curValue) {
return preValue > curValue ? preValue : curValue; // 比较后,返回大的值
});
};
// 最小值
Array.prototype.min = function () {
return this.reduce(function (preValue, curValue) {
return preValue > curValue ? curValue : preValue; // 比较后,返回小的值
});
};
var arr = [2,4,10,7,5,8,6];
console.log(arr.min()); // 2
console.log(arr.max()); // 10
- 数组去重
function arrayUnique(array) {
var obj = {}, type;
return array.reduce(function (preValue, curValue) {
type = typeof curValue;
if (!obj[curValue]) {
obj[curValue] = [type];
preValue.push(curValue);
} else if (obj[curValue].indexOf(type) < 0) { // 判断数据类型是否存在,如1和"1"
obj[curValue].push(type);
preValue.push(curValue);
}
return preValue;
}, []);
}
var array = [1, 4, 5, 7, 4, 8, 1, 10, 4, '1'];
console.log(arrayUnique(array)); // [1, 4, 5, 7, 8, 10, "1"]