来吧!一文彻底搞懂引用类型!

2020-02-13 13:28:13 浏览数 (1)

作者 | Jeskson

掘金 | https://juejin.im/user/5a16e1f3f265da43128096cb

2020年01月09日

引用类型的值(对象)是引用类型的一个实例。引用类型有时也称对象定义,因为它们描述的是一类对象所具有的属性和方法。

对象是某个特定引用类型的实例,新对象是使用new操作符后跟一个构造函数来创建的,构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。

ver dada = new Object();

JavaScript对象划分为:

引用类型,基本包装类型,内置对象

JavaScript引用类型分:Object类型,Array类型,RegExp类型,Date类型,Function类型。

基本包装类型,Boolean类型,Number类型,String类型。

Object类型

Object类型是JavaScript中常见的一种类型,使用new操作符

vardada =newObject();dada.name ="dada";dada.age =12;

使用对象字面量表示法:

vardada = {name:'dada',age:12,}

访问对象的属性

console.log(dada.name);console.log(dada['name']);

vardadaname ="dada";console.log(dada[dadaname]);

注意,除非是必须非要使用变量来访问属性,否则不建议使用,还是建议使用点方法来表示。

new运算符的作用就是创建一个对象的实例,这个对象可以是用户自定义,也可以是带有构造函数的一些系统自带的对象。

创建Object类型有两种方法:

第一,使用new运算符创建Object,第二,使用对象字面量方式创建Object,使用最多。

判断js对象是否拥有这个属性,第一种,使用in判断对象的所有属性,包含对象实例以及其原型的属性;第二种,使用

Object.hasOwnProperty(proName);判断的是对象的实例是否具有某个属性。

引用类型通常叫做类,即是,遇到引用值,可以理解为对象。

对象是由 new 运算符加上要实例化的对象的名字创建的

varo =newObject();

Object对象具有的属性

constructor对创建对象的函数的引用(指针),对于Object对象,该指针指向原始的Object()函数。

检测数组

instanceof

Array.isArray(value)

Object.prototype.toString.call(obj)

转换方法

toString() 返回字符串

valueOf() 返回数组本身

toLocaleString()

join()

vardada = ['da1','da2'];dada.toString()"da1,da2"dada.valueOf()['da1','da2']

添加移除

push()数组末尾接收任意数量,并返回修改后的数组的长度。

pop()数组末尾移除最后一项,减少length值,返回移除的项

shift()移除数组第一项,并返回该项,数组长度减一

unshift()数组前面添加任意项并返回新的数组的长度

重排序

reverse() 反转

sort()排序

vararr = [1,3,7,9,4,5]vararr2 = arr.sort(function(a,b){// 从小到大的排序returna-b// 从大到小的排序returnb-a})console.log(arr2);

操作方法,concat()创建当前数组的一个副本,如果有参数则添加这个副本的末尾,如果没有参数就返回当前数组的副本。

slice(start,end)接收一个或两个参数,但不返回结束位置的项,不会影响原数组;splice(起始位置,删除的项数量,要插入的项),删除,替换,插入。

dada.splice(1, 0, 'da1','da2');

// dada从1位置插入两项

dada.splice(1,1, 'da1','da2');

// 为在1位置删除1项,插入两项

indexOf()从数组开头向后查找

lastIndexOf()从数组的末尾开始向前查找

返回的是查找项在数组中的位置,如果没有找到就返回-1

基本类型值和引用类型值,基本类型值是简单的数据,而引用类型是指由多个值构成的对象。

基本类型,undefined,null,boolean,number,string

引用类型,Object,Array等

从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份到新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,这个指针指向存储在堆中的一个对象,复制后的结果是,两个变量实际上将引用同一个对象,两个变量对象指向堆内存中同一个Object对象,所以当改变其中一个变量,就会影响另一个变量。

vardada =newObject();vardashu = dada;dada.name='da1';console.log(dashu.name);// da1

数值的传递,函数的参数

在JavaScript中所有函数的参数都是使用值传递的,就是把函数外的值复制传递给函数内部的参数,就好像从一个变量赋值到另一个变量一样

基本类型值的传递如同基类型变量的复制效果一样,引用类型的值传递,就如同引用类型变量的复制一样的效果。

在向参数传递基本类型值时,被传递的值会复制给一个局部变量,在向参数传递引用类型的值时,会把这个值在内存中的地址复制给这个局部变量。

基本类型

functionadd(num){ num =1;returnnum;}varda1 =12;varresult = add(da1);console.log(da1);console.log(result);

引用类型

functionsetName(obj){ obj.name ='dada';}varperson =newObject();setName(person);console.log(person.name);dada

typeof检测基本类型

typeof中 var da; // 检测为undefined

typeof中 var da = null; // 检测为object

typeof中 var da = new Object(); // 检测为object

instanceof检测引用类型的值

如果不是检测的不是引用类型的值,而是基本类型的值,则返回false

如果是引用类型的值,返回为true

vard =newNumber(11);console.log(dinstanceofNumber);true

Object.prototype.toString表示返回一个表示该对象的字符串

Object.prototype.toString.call(null);

"[object Null]"

Object.prototype.toString.call(1111);

"[object Number]"

Object.prototype.toString.call('string');

"[object String]"

总结Object类型,大部分引用类型值都是Object类型的实例,创建Object实例的方式有两种,一种是使用new操作符后跟Object构造函数,一种是使用对象字面量表示法。

array类型,Date类型,RegExp类型,Function类型,基本包装类型是什么呢?

我相信你在学习时一定见到过Array类型,也是最常见的,数组的每一项可以存储任何类型的数据,数组的大小是可以动态变动的,数组的长度越大就可以存储更多的数据。

那么第一问,数组是怎么创建的呢?

数组有哪些自带的属性,如何检查是否为一个数组,数组元素的增删改等,数组与字符串的相互转化,数据的一些方法,如,截取,合并,排序,查找数组元素的元素,如何遍历数组,进行迭代等。

数组的创建,同样数组也有两种基本的创建方式,一种是使用array构造函数,和创建对象一样new Object()方法。看看使用Array构造函数的语句代码:

varda =newArray();newArray();// 创建一个数组newArray([size]);// 创建一个数组并指定长度newArray(element0, element1, ..., elementn);// 创建一个数组并赋值

采用字面量的方法

varda = [];// 创建一个空数组varda1 = [1,2,3];// 创建一个有三个元素的数组

varda1 = [1,2,3];console.log(da1instanceofArray);VM272:1true

vardada = [1,2,3];undefinedconsole.log(Array.isArray(dada));VM422:1true

检查是否是数组的两种方法:

一种使用instanceof,一种使用Array.isArray()方法。

constructor// 返回创建数组对象的原型函数length// 返回数组的长度prototype// 可以增加数组的原型方法和函数

可以看出一个数组中方法:

_proto_concatconstructorcopyWithinentrieseveryfillfilterfindfindIndexflatflatMapforEachincludesindexOfjoinkeyslastIndexOflengthmappoppushreducereduceRightreverseshiftslicesomesortsplicetoLocaleStringtoStringunshift

数组的length属性,返回数组的长度。

vararray =newArray();undefinedconsole.log(array.constructor);VM576:1ƒArray() { [native code] }undefinedconsole.log(array.constructor ===Array);VM615:1trueundefined

constructor属性返回对创建此对象的数组函数的引用,即是返回对象相对应的构造函数。

array.push(元素1,元素2,...)将一个或多个元素添加到数组的末尾,并返回新数组的长度。

array.unshift(元素1,元素2,...)将一个或多个元素添加到数组的开头,并返回新数组的长度。

array.pop()从数组中删除最后一个元素,并返回最后一个元素的值。

原始数组的最后一个元素被删除。

array.shift()删除数组的第一个元素,并返回第一个元素。

原始数组的第一个元素被删除。

array.splice(start 第一个参数为指定开始修改的位置,如果超出了数组的长度就从末尾开始添加内容,如果为负值,就从数组末尾开始的第几位,从1开始计数, deleteCount第二个参数为,要删除的元素的个数,是从start位置开始要删除的元素个数,为0时表示不删除元素,其后的参数为要加入的元素,从start位置开始,添加的元素。

数组转为字符串

array.join('')

所有对象都具有toLocaleString(),toString(),valueOf()方法

varda = [1,23,4];undefinedconsole.log(da.toString());VM727:11,23,4undefinedconsole.log(da.valueOf());VM813:1(3) [1,23,4]undefinedconsole.log(da.toLocaleString());VM861:1undefined

toString()方法(toLocaleStirng方法),返回这个数组中每个值的字符串形式,拼接而成的一个以逗号分隔的字符串,valueOf()方法,返回的是数组本身。

当有一个值为null或者是undefined时,在join()方法,toStirng()方法和valueOf()方法,toLocaleString()方法的返回则都是以空字符串表示。

字符串变化为数组

string.split(第一个参数为字符串或者是正则表达式,从该参数指定的地方对字符串进行分割,第二个参数为指定返回的数组的最大长度)用于把一个字符串分割成字符串数组

数组的截取与合并

array.slice(start,end)方法,从数组中返回start下标开始,直到end下标结束(不包含),该方法不会改变原始数组,只会返回一个新的子数组。

vararr = [2,23,3,23];undefinedarray.slice(-1);[]console.log(arr);VM1019:1(4) [2,23,3,23]undefinedarr.slice(1,3)(2) [23,3]console.log(arr);VM1131:1(4) [2,23,3,23]undefined

可以看出这个方法不会修改原始数组,只是会返回一个新的子数组,如果想要删除数组中的元素,可以使用array.splice()。

数组的合并

array.concat()方法

sort()方法用于对数组的元素进行排序,并返回原数组。

不带参数,按照字符串UniCode码的顺序进行排序。

constarray= ['a','d','c','b'];array.sort();//['a', 'b', 'c', 'd']

按照数值大小进行排序-升序

array.sort((a,b) => {returna-b;// 从小到大排序});

按照数值大小进行排序-降序

array.sort((a,b)=>{returnb-a;// 从大到小排序});

array.reverse()方法

reverse() 方法将数组中元素的位置颠倒,第一个数组元素成为最后一个数组元素,最后一个数组元素成为第一个。

数组的sort()和reverse()方法都对原数组进行了修改,返回值是经过排序之后的数组。

find(callback[, thisArg])方法,用于找出第一个符合条件的数组元素。

[1,3,-5,20].find((n) =>n <0)// -5

findIndex(callback[, thisArg])返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

[1,3,13,25].findIndex(function(value, index, arr){returnvalue >9;})// 2

functionf(value){returnvalue >this.age;}letperson = {name:'dada',age:20};[1,12,25,5].find(f, person);// 25

includes(第一个参数为要查找的元素,第二个参数为开始查找的索引位置)方法,返回一个布尔值,表示是否存在给定值在该数组中。

array.filter()方法,array.every()方法检测数组中的每个元素是否通过测试的,如果测试通过为true。

arr.some()判断数组汇总是否包含可以通过测试的元素怒,和every不同的是,这里只要某一个元素通过测试,即返回true。

[2,3,15,1,4].some(item=>item >6);// true

leta = [1,2,3,4,5];letb = [];a.forEach((item) =>{ b.push(item 1);});console.log(b);// [2,3,4,5,6]

keys()是对键名的遍历、values()对键值的遍历、entries()是对键值对的遍历。

for(letitemof['a','b'].keys()){ consloe.log(item);//0//1}for(letitemof['a','b'].values()){ consloe.log(item);//'a'//'b'}letarr = [0,1];for(letitemofarr.entries()){console.log(item);// [0, 0]// [1, 1]}

consttotal = [0,1,2,3].reduce((a, b) =>{returna b;},0);// total is 6

constda = [[0,1], [2,3], [4,5]].reduce((a, b) =>{returna.concat(b);}, []);// da is [0, 1, 2, 3, 4, 5]

Array.from()方法可以用来将json数组格式转换为数组

letda = {'0':'a','1':'b','2':'c',length:3};letarr =Array.from(da);// ['a', 'b', 'c']

Array.of()方法可以将一组值转换为数组

letda =Array.of(1,2,3,4);console.log(da);//[1,2,3,4]

console.log(...[1,2,3])// 1 2 3console.log(1, ...[2,3,4],5)// 1 2 3 4 5

栈方法,队列方法

数组的表示像栈一样,栈是一种后进先出的数据结构。

栈中项的插入(叫做推入)和移除(叫做弹出),只发生在一个位置——栈的顶部。

push()方法可以接收任意数量的参数,在数组的末尾逐个添加,并返回修改后数组的长度,pop()方法可以从数组末尾移除最后一项,返回移除的项。

队列方法

队列数据结构的访问方法是先进先出,队列在列表末端添加项,从列表的前端移除项。

every()方法,传入的函数必须对每一项返回true,才会返回true,否则它就返回false,some()方法则是只要传入的函数对数组中的某项返回true,就会返回true。

Object对象具有的属性

constructor

对创建对象的函数的引用(指针),对于Object对象,该指针指向原始的Object()函数。

Prototype

对该对象的对象原型 的引用,对于所有的对象,它默认返回Object对象的一个实例。

方法:

hasOwnProperty(property)

判断对象是否有某个特定的属性,必须要字符串指定该属性

IsPrototypeOf(object)

判断该对象是否为另一个对象的原型

PropertyIsEnumerable

判断给定的属性是否可以用for...in语句进行枚举

toString()返回对象的原始字符串表示。

valuOf()方法返回最合适该对象的原始值。

reduce()方法从数组的第一项开始,逐个遍历到最后。

reduceRight()方法从数组的最后一个项开始,向前遍历到第一项。

JavaScript引用类型中的Date对象

Date 类型使用自 UTC(Coordinated Universal Time,国际协调时间)1970 年 1 月 1 日午夜(零时)开始经过的毫秒数来保存日期。

创建Date对象,使用new 操作符和Date构造函数:

vardate =newDate();vardate =newDate("1178/08/06 04:20:00");vardate =newDate(154656847478617);

静态方法,Date.now()方法返回当前时间距离时间零点,1970年1月1日00:00:00UTC的毫秒数,相当于Unix时间戳乘以1000。

getTime()方法

返回实例距离1970年1月1日00:00:00的毫秒数,等同于valueOf方法

Date类型使用UTC国际协调时间1970年1月1日午夜(零时)开始经过的毫秒数来保存日期。如果调用Date构造函数而不传递参数,则新创建的对象获取的是当前的日期和时间。

如果想要特定的日期和时间创建日期对象,传入参数。

// 提供了两个方法Date.parse()Date.UTC()

Date.parse()方法接收一个表示日期的字符串参数

varnewDate =newDate(Date.parse('May 23, 2010'));

Date.UTC()方法返回表示日期的毫秒数。

getFullYear() 返回四位的年份getMonth() 返回月份(0 ~ 11,0表示1月,11表示12月)getDay() 返回星期几,星期日为0,星期一为1,以此类推getDate() 返回实例对象对应每个月的几号(从1开始)getHours() 返回小时数 (0 ~ 23)getMinutes() 返回分钟数 (0 ~ 59)getSeconds() 返回秒数 (0 ~ 59)getMilliseconds() 返回毫秒数 (0 ~ 999)setTime(milliseconds) 设置毫秒时间戳setYear(year) 设置距离1900年的年数setFullYear(year[, month, date]) 设置四位年份setMonth(month[, date]) 设置月份(0-11)setDate(date) 设置实例对象对应的每个月的几号(1-31),返回改变后毫秒时间戳setHours(hour[, min, sec, ms]) 设置小时(0-23)setMinutes(min[, sec, ms]) 设置分钟(0-59)setSeconds(sec[, ms]) 设置秒(0-59)setMilliseconds() 设置毫秒(0-999)

toDateString()

显示格式星期几,月,日,年

toTimeString()

显示时,分,秒和时区

toLocalDateString()

显示星期几,月,日,和年

toLocaleTimeString()

显示实现的格式显示时,分,秒

toUTCString()

显示完整的UTC日期

RegExp类型

ECMAScript 通过 RegExp 类型来支持正则表达式。

varexpression =/ pattern /flags ;// 模式(pattern)部分可以是任何简单或复杂的正则表达式// 标志(flags)用以标明正则表达式的行为

g表示全局模式,即是模式将被应用于所有字符串

i表示不区分大小写模式

m表示多行模式

RegExp 构造函数接收两个参数:

一个是要匹配的字符串模式,一个是可选的标志字符串。

RegExp实例方法

exec()方法,用于检索字符串的正则表达式的匹配。

string,检索的字符串,返回一个数组,没有则返回null

/d{2,4}/ //匹配2~4个数字

/w{3}d?/ //精确匹配三个单词和一个可选的数字

函数,函数其实是对象,每个函数都是Function类型的实例。

韩式是定义一次就可以调用或者是执行任意多次的一段代码,通过函数可以封装很多代码块,而且可以在任何地方,任何时候调用,函数本身没有运行功能,只有调用才能运行。

函数实际上是对象,每个函数都是Function类型的实例

函数名是指向函数对象的指针,不会与某个函数绑定,使用不带有圆括号的函数名是访问函数指针,不是调用函数。

在函数中,有两个特殊的对象,arguments和this。

functionmethod(){// 没有参数的函数console.log("da");}functionmethod(name){// 带有参数的函数console.log("da1");}

函数声明:

使用函数声明语法

functionmethod(name){returnname;}

使用变量初始化函数

varda =function(name){returnname;}

使用function构造函数

varda1 =newFunction(name);

varadd=newFunction(‘num1’,’num2’,’return num1 num2’);// 不推荐这种方式,因为会导致解析两次代码,// 第一次解析常规ECMAscript代码(全部代码),// 第二次解析传入构造函数中的字符串,从而影响性能。

参数,把函数的返回值作为参数传递,把函数本身作为参数传递。

varresult=add(sum(10),10);varresult=add(sum,10);//sum是一个函数

return返回值,函数也可以不带有返回值,将返回undefined

函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

定义函数,第一,使用函数声明语法,第二,使用函数吧表达式,第三,使用function构造函数。

函数声明与函数表达式的区别

解释器会先读取函数声明,在使其在执行之前可以访问。

函数声明语法

functionsum(num1, num2){returnnum1 num2;}

使用函数表达式则必须要等到解析器执行到它所在的代码行时,才会执行,变量声明提前,而值留在原地。

console.log(sum(10,10));functionsum(num1, num2){returnnum1 num2;}

在代码执行前,解析器会通过函数声明提升,读取并将函数声明添加到执行环境中,放入到代码树的顶部。

console.log(sum(10,10));// 报错varsum =function(num1, num2){returnnum1 num2;};

此代码函数位于一个初始化语句中,不是函数声明,不会提前,会把var sum提前,也就是在执行到函数所在语句之前,变量sum中不会保存对函数的引用。

函数是对象来着,函数名是指针。函数名仅仅是指向函数的指针,所以,一个函数对象可以有多个名字。

没有重载的概念

functiondada(num){returnnum 100;}functiondada(num){returnnum 200;}varresult = dada(100);//300// 后面的函数覆盖了前面的函数

以上代码实际上与下面的代码没有什么区别:

varda1=function(num){returnnum 100;};da1=function(num){returnnum 200;};varresult = da1(100);//300

函数作为参数传递给另一个函数

要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号;从一个函数中返回另一个函数。

函数内部的属性

arguments是个类数组对象

传入函数的所有参数,对象有一个叫callee的属性,属性值是一个指针,指向拥有arguments对象的函数本身。

this对象,指向函数执行的环境对象,在哪个对象中执行,就指向哪个对象。

caller属性,保存着调用当前函数的函数的引用

如果是在全局作用域中调用当前函数,它的值为 null。

functionouter(){ inner(); }functioninner(){console.log(inner.caller); } outer();/*输出结果:

ƒunction outer(){

inner();

}

*/

prototype属性:

是一个对象,通过调用构造函数而创建的一个对象,可以由特定类型的所有实例共享的属性和方法。

toString()方法将函数体转换成对应的字符串。

bind()方法:

会创建一个函数的实例,其 this 值会被绑定到传给 bind() 函数的值。

apply()与call()方法

两个方法的作用都是在特定的作用域中调用函数

实际上就是设置函数内部的this值

apply():接收两个参数

一个是函数运行的作用域,

一个是参数数组,可以是数组,也可以是arguments这个类数组对象;

在使用 call()方法时,传递给函数的参数必须逐个列举出来;

区别仅在于接收参数的方式不同。

apply() 和 call() 真正的本质是用来,扩充函数赖以运行的作用域。

基本包装类型

为了便于操作基本数据类型,ECMAScript还提供了3个特殊的引用数据类型:Boolean、Number和String。

引用类型与基本包装类型的区别,在于它们的对象生命周期不同:引用类型:使用new创建引用类型的实例,在执行数据流离开当前作用域时会一直存储在内存中。基本包装类型:自动创建基本包装类型的对象,只执行一行代码的瞬间之后就会立即销毁。这意味着在运行时为基本包装类型值添加属性和方法是无效的。

创建Boolean对象

// 定义了一个名为 da 的布尔对象varda =newBoolean(true);

如果布尔对象无初始值或者其值为:0、-0、null、""、false、undefined、NaN,那么对象的值为false,否则,其值为true

在布尔表达式中,所有的对象都会被转换为true。

引用类型的布尔值,其实是对象,所以在布尔表达式中使用Boolean 对象都会返回为true。

varda =newBoolean(false); alert(typeofda);// object varda1 =false; alert(typeofda1);// boolean

varda =newBoolean(false); alert(dainstanceofBoolean);// true varda1 =false;alert(da1instanceofBoolean);// false

toString()方法

功能:根据布尔值返回字符串 "true" 或"false"。

valueOf()方法

功能:返回 Boolean 对象的原始值,就是返回基本类型值

true 或 false。

创建Number对象

Number类型是数字值创建的引用类型。

varda =newNumber(10);

toFixed()方法

Number类型的toFixed()方法可以接受一个数值,表示保留的小数的个数(会自动四舍五入)。

toString()方法

将Number数值转换为字符串,该方法接受一个可选参数基数,告诉它返回几进制数值的字符串形式,若省略,则默认基数为10,即十进制。

valueOf()方法

valueOf()返回一个 Number 对象的基本数字值。

创建String对象

String类型是字符串值创建的引用类型。

varda =newString("hello world");

charAt()和 charCodeAt()两个方法都接收一个参数

即基于 0 的字符位置

charAt():

以单字符字符串的形式返回给定位置的那个字符

charCodeAt():

返回给定位置的那个字符的字符编码

截取字符串的方法:

slice()、substr()和 substring()

这三个函数,都不改变原本的字符串,只是在原先的字符串上创建了一个副本,返回操作副本后的值。

slice()方法会将传入的负值与字符串的长度相加substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为0substring()方法会把所有负值参数都转换为0。

indexOf()

方法从索引 0 的位置开始查询子字符串

lastIndexOf()

方法从最后一个索引开始查找字符串

trimLeft()和 trimRight()方法,

分别用于删除字符串开头和末尾的空格。

字符串大小写转换的方法:toLowerCase()、toUpperCase()var stringValue ="hello world"; alert(stringValue.toUpperCase());//"HELLO WORLD"alert(stringValue.toLowerCase());//"hello world"

match()方法

vars ='hello21 world21';console.log(s.match(/d{2}/));// [ '21', index: 5, input: 'hello21 world21' ]

parseInt():转换为整型;

parseFloat():转换为浮点型;

isNaN():判断为是否不是数值;

encodeURI():对字符串进行编码;

decodeURI():

对encodeURI()函数编码过的字符串进行解码。

内置对象:

ECMA-262对内置对象是,由ECMAScript实现提供的,不依赖宿主环境的对象。

程序执行之前就已经存在了。不需要自己定义,不需要自己实例化。

定义了两个内置对象:Global和Math。

0 人点赞