重读JS(五)引用类型

写在文章之前

博主编程入门学的是c和java,所以有些概念还拐不过来,本文开篇需要先来了解下面向过程和面向对象、对象、类、和实例

本章内容

  • 使用对象
  • 创建并操作数组
  • 理解基本的JavaScript类型
  • 使用基本类型和基本包装类型

🥑 重点

Array方法集,Date方法集
函数:arguments this,prototype,apply()和call(),bind()
基本包装类型:Boolean,Number,String,String类型方法集
单体内置对象:Global,Math,Math方法集

正文

划重点:引用类型的值(对象)是引用类型(类)的一个实例
在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它也常被称为类,但这种称呼并不妥当。尽管ECMAScript从技术上讲是一门面向对象的语言,但它不具备传统的面向对象所支持的类和接口等基本构造。它们不是想通的概念,因此本文不再使用类这个概念。
引用类型有时候也被称为对象定义,因为他们描述的是一类对象所具有的的属性和方法。

var person = new Object()

这行代码创建了Object引用类型的一个新实例,然后把该实例保存在了变量person中。使用的构造函数时Object,它只为新对象定义了默认的属性和方法。ECMAScript提供了许多原生引用类型(例如Object),以便开发人员用以实现常见的计算任务。

new的作用:

  • 创建对象,实例化对象
  • 实例化对象,赋予对象空间,即堆内存地址
  • 调用构造函数

一、Object类型

目前为止,我们看到的大多数引用类型都是Object类型的实例;而且Object也是ECMAScript中使用最多的一个类型,虽然Object的实例不具备多少功能,但在应对存储和传输数据而言,是非常理想的选择。

创建

new一个

var person = new Object();
person.name = "Nicholas",
person.age = 18

对象字面量语法,用于简化创建包含大量属性的对象的过程。通过其定义对象时,实际上不会调用Object构造函数。

属性名也可以用字符串:"name":"Nicholas",且数值属性名会自动转为字符串

//对象字面量语法
var person = {
    name:"Nicholas",
    age:18//不能添加逗号,ES6中可以
}

访问

点表示法

//点表示法
person.name

括号表示法

优点是可通过变量来访问属性,如果属性名包含会导致语法错误的字符,或者使用的是关键字或保留字,也可以使用这种方法

//括号表示法
var propertyName = "name"
person[propertyName]
person["first name"]  //含有铬空格,不能用点表示法来访问

二、Array类型

ECMAScript数组的每一项可以保存任何类型的数据,且数组的大小可以动态调整。

创建与访问

使用数组字面量表示法时,也不会调用Array构造函数。(Firefox3及更早版本除外)

//new一个
var colors = new Array();
var colors = new Array(3);
var colors = new Array("red","blud","green");
var colors = Array()

//数组字面量表示法
var colors = ["red","blud","green"];
var colors = [1,2,];   // × 不要这样,会创建一个包含2或3项的数组 
colors[0]   //red

length的巧用

很方便的在数组末尾添加新项

colors[colors.length] = "black"

Array.isArray()

检测数组问题引入:
instanceof操作符它假定单一的全局执行环境。如果网页包含多个框架,实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。怎么解决呢?

ECMAScript5新增Array.isArray(array)。这个方法最终确定某个值到底是不是数组,而不管它在哪个全局执行环境中创建的。

数组方法总结

补充:
.some() 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。

.slice() 不改变原数组

缩小方法:reduce()和reduceRight,这两个方法都会迭代数组的所有相,然后构建一个最终返回的值。

var values = [1,2,3,4,5]
var sum = values.reduce(function(){
    return prev + cur;
});
alert(sum)  //15

sort()补充

.sort() 为了实现排序,会调用每个数组项的toString()转型方法,然后比较得到的字符串。

var value = [0,1,5,10,15]
value.sort(),
values //0,1,10,15,5

如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。

❗ ❗ 内部原理还没搞懂

//升序
function compare(value1,value2){
    if(value1 < value2){
        return -1;   //想要value1位于value2之前,则返回负数
    }else if(value1 > value2){
        return 1;    //想要value1位于value2之后,则返回正数
    }else{
        return 0;
    }    
}

var value = [0,1,5,10,15]
value.sort(compare),
values //0,1,5,10,15

对于数值类型或者其valueOf()方法会返回数值类型的对象类型,可以使用更简单的比较函数:

//升序
function compareNumbers(a, b) {
  return a - b;
}

三、Date类型

new Date()

new Date()中的参数要么没有,要么是表示日期的毫秒数(1970年1月1日午夜开始的),Date.parse()和Date.UTC()可以。

var myDate = new Date();   //2020-03-28T09:32:11.510Z
//Date.parse()和Date.UTC()
var myDate = new Date(Date.parse("May 25,2004"));  //2004-05-24T16:00:00.000Z
var myDate = new Date("May 25,2004");  
var myDate = new Date(Date.UTC(2000,0));  
var myDate = new Date(Date.UTC(2000,4,5,17,55,55)); //2000-05-05T17:55:55.000Z
//基于本地时区
var myDate = new Date(2000,0);
var myDate = new Date(2005,4,5,17,55,55)

Date.now()

//取得开始时间
var start = Date.now()

doSomething()

//取得停止时间
var stop = Date.now()

继承方法

与其它引用类型一样,Date也重写了toLocaleString()、toString()和value()方法。但这些方法返回值与其他类型中的方法不同。这些方法的输出因浏览器而异

var myDate = new Date()
alert(myDate.toLocaleString())  //2020-3-28 18:05:38
alert(myDate.toString())  //Sat Mar 28 2020 18:05:38 GMT+0800 (GMT+08:00)
alert(myDate.valueOf())  //1585389938346

日期格式化

这些方法的输出因浏览器而异。
toDateString() 以特定于实现的格式显示星期几、月、日和年;
toTimeString() 以特定于实现的格式显示时、分、秒和时区
toLocalDateString() 以特定于地区的格式显示星期几、月、日和年
toLocalTimeString() 以特定于实现的格式显示时、分、秒
toUTCString() 以特定于实现的格式完整的UTC日期

常用方法

myDate.getYear(); //获取当前年份(2位) 
myDate.getFullYear(); //获取完整的年份(4位,1970-????) 
myDate.getMonth(); //获取当前月份(0-11,0代表1月) ,所以获取当前月份是myDate.getMonth()+1;  
myDate.getDate(); //获取当前日(1-31) 
myDate.getDay(); //获取当前星期X(0-6,0代表星期天) 
myDate.getTime(); //获取当前时间(从1970.1.1开始的毫秒数) 
myDate.getHours(); //获取当前小时数(0-23) 
myDate.getMinutes(); //获取当前分钟数(0-59) 
myDate.getSeconds(); //获取当前秒数(0-59) 
myDate.getMilliseconds(); //获取当前毫秒数(0-999) 
myDate.toLocaleDateString(); //获取当前日期

Regex类型

关于JavaScript中的正则表达式使用,推荐看《JavaScript正则表达式迷你书》,看完一遍后我会再做详细的总结。
在这里不做太多笔记。

创建

var expression = /pattern/flags

pattern:可以是任意简单或者复杂的正则表达式
flagsg表示全局,而非发现第一个匹配项时立即停止;i表示不区分大小写;m表示多行模式,即在达到一行文本末尾时还回继续查找下一行

实例属性

constructor 返回一个函数,该函数是一个创建 RegExp 对象的原型。
global 判断是否设置了 "g" 修饰符
ignoreCase 判断是否设置了 "i" 修饰符
lastIndex 用于规定下次匹配的起始位置
multiline 判断是否设置了 "m" 修饰符
source 返回正则表达式的匹配模式

实例方法

regex.exec(str) 返回找到的值(数组),并确定位置
regex.test(str) 检测一个字符串是否匹配某个模式
regex.toString() 返回正则表达式的字符串

五、Function

每个函数都是Function类型的实例,而且斗鱼其他引用类型一样具有属性和方法。由于函数时对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定

创建

函数声明语法定义

function sum(num1,num2){
    return num1 + num2;
}

函数表达式定义

var sum = function (num1,num2){
    return num1 + num2;
};

解析器在向执行环境中加载数据时,会率先读取函数声明,并使其在执行任何代码之前可访问
至于函数表达式,则博旭等到解析器执行到它所在的代码行中,才会真正被解释执行。

alert(sum());
function sum(){
    //...
}

理解函数名实际上也是一个指向函数对象的指针,看下面这段代码

function sum(num1,num2){
    return num1 + num2;
}

alert(sum(10,10))  //20

var anotherSum = sum;
sum = null;
alert(anotherSum(10,10))  //20

没有重载(深入理解)

第三章例子:

function addSomeNumber(num){
    return num + 100;
}

function addSomeNumber(num1,num2){
    return num1 + num2;
}

联系函数名实际上也是一个指向函数对象的指针,可看成下面代码:

function addSomeNumber(num){
    return num + 100;
}

addSomeNumber = function(num1,num2){   //表面上是覆盖原函数,实际是指针指向改变
    return num1 + num2;
}

作为值的函数

函数的内部属性

arguments、arguments.callee

arguments的主要用途是保存函数参数,这个对象还有一个叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。例子:

function factorial(num){
    if(num <= 1){
        return 1;
    }else{
        //return num*factorial(num-1)
        return num*arguments.callee(num-1) 
    }
}

在这个重写后的factorial()函数的函数体内,没有再引用函数名factorial。这样,无论引用函数时使用的是什么名字,都可以保证正常完成递归调用。

this

this引用的是函数据以执行的环境对象——或者也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的就是window)。示例:

window.color = "red"
var o = {color: "blue"}

function sayColor() {
    alert(this.color)
}

sayColor();  //red

o.sayColor = sayColor;
o.sayColor();  //blue

牢记:函数的名字仅仅是一个包含指针的变量而已。因此,即使是在不同的环境中执行,全局的sayColor()函数与o.sayColor()指向的仍然是同一个函数。

caller

这个属性中保存着边勇当前函数的函数的引用。如果是在全局作用域中调用当前函数,它的值为null。

函数属性和方法

ECMAScript中的函数是对象,因此也有属性和方法。每个函数都包含两个属性:length和prototype。

length

length属性表示函数希望接收的命名参数的个数。

prototype

在ECMAScript核心所定义的全部属性中,最耐人寻味的就要数prototype属性了。

对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。诸如toString()和valueOf()等方法实际上都保存在prototype名下,只不过通过各自对象的实例访问罢了。

在创建自定义引用类型以及实现继承时,prototype属性是极为重要的。(第六章详细介绍)

在ECMAScript5中,prototype属性是不可枚举的,因此使用for-in无法发现。

apply()和call()

每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。

首先,apply()方法接受两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。例如:

function sum(num1,num2){
    return num1 + num2;
}

function callSum1(num1,num2){  //传入arguments对象
    return sum.apply(this,arguments)
}

function callSum2(num1,num2){
    return sum.apply(this,[num1,num2])  //传入数组
}

alert(callSum1(10,10))  //20
alert(callSum2(10,10))  //20

这个例子中,callSum1()在执行sum()函数时传入了this作为this值(因为实在全局作用域中调用的,所以传入的就是window对象)和arguments对象。

call()方法与apply()方法的作用相同,区别仅在于接受参数的方式不同。call()方法第一个参数是this值,不同的是其余参数都直接传给函数。即传递给函数的参数必须逐个列出来。例子:

function sum(num1,num2){
    return num1 + num2;
}

function callSum1(num1,num2){  //传入arguments对象
    return sum.call(this,num1,num2)
}

alert(callSum1(10,10))  //20

它们真正强大的地方是能够扩充函数来一运行的作用域,好处就是对象不需要与方法有任何耦合关系。例子:

window.color = "red"
var o = {color:"blue"}
function sayColor(){
    alert(this.color)
}
sayColor()  //red
sayColor.call(this)  //red
sayColor.call(window)  //red
sayColor.call(o)  //blue

bind()

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

window.color = "red"
var o = {color:"blue"}
function sayColor(){
    alert(this.color)
}
var objectSayColor = sayColor.bind(o)
objectSayColor()  //blue

在这里,sayColor()调用bind()并传入对象,创建了objectSayColor()函数。objectSayColor()函数的this值等于o,因此即使是在全局作用域中调用这个函数,也会看到"blue"。

继承方法

toLocaleString()和toString()方法始终返回函数的代码。

补充:toString()方法返回函数代码本身,以前会省略注释和空格。ES6开始就不会了

六、基本包装类型

为了便于操作基本数据类型,ECMAScript还提供了3个特殊的引用类型:Boolean、Number和String。这些类型与本章介绍的其它引用类型相似,但也具有各自的基本类型相应的特殊行为。基本类型不是对象,逻辑上不应该有方法。其实,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。例子:

var s1 = "some text"
var s2 = s1.substring(2)

为了实现以上代码,后台自动完成下列处理(也适用于Boolean和Number类型对应的布尔值和数字值):

  • 创建String类型的一个实例
  • 在实例上调用指定的方法
  • 销毁这个实例
var s1 = new String("some text")
var s2 = s1.substring(2)
s1 = null

当然,可以显示地调用Boolean、Number和String来创建基本包装类型的对象。但应该在绝对必要的情况下再这样做,不然会分不清在处理基本类型还是引用类型。以下代码说明使用new调用基本包类型的构造函数,与直接调用同名的转型函数时不一样的:

var value = "25";
var number = Number(value)
alert(typeof(number))   //number

var obj = new Number(value)
alert(typeof(obj))    //Object

❓这句话的理解:基本包装类型的实例调用typeof返回"object",而且所有基本包装类型的对象都会被转换为布尔值true。

Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。例子:

var obj = new Object("some text")
alert(obj instanceof String)  //true

Boolean类型

建议永远都不要使用

Number类型

重写valueOf()、toLocaleString()和toString()方法

valueof()返回对象表示的基本类型的数值,另外两个返回字符串形式。

toString(参数),参数告诉它返回几进制数值的字符串形式。

方法

.toFixed(小数位数) //返回含小数位数值的字符串形式
.toExponential(小数位数) //返回以指数表示法(e)表示的数值:1.0e+1
.toPrecision(所有数字的位数) //返回固定大小(fixed)格式,也可能返回指数格式,具体看哪种更合适。

String类型

继承的valueOf()、toLocaleString()和toString()方法返回对象所表示的基本字符串值。

1.字符方法

.length
.charAt(1) //返回固定位置的字符,0起始
.charCodeAt(1) //返回固定位置的字符的字符编码
.stringValue(1) //同charAt,但IE7及更早返回undefined

2.字符串操作方法

str1.concat(str2,str3,...) //拼接,返回新字符串

slice()、substr()、substring()都会返回被操作字符的一个子字符串,都接受一或两个参数。
参数一:指定子字符串的开始位置
参数二:子字符串到哪里结束,三个有所区别:
slice()和substring()指定的是子字符串最后一个字符后面的位置,也就是所说的含头不含尾。
substr()指定的是返回字符的个数

var stringValue = "hello world"
alert(stringValue.slice(3))        //lo world
alert(stringValue.substring(3))    //lo world
alert(stringValue.substr(3))       //lo world
alert(stringValue.slice(3,7))      //lo w
alert(stringValue.substring(3,7))  //lo w
alert(stringValue.substr(3,7))     //lo worl

参数为负数的时候

var stringValue = "hello world"

//.slice(-3+length)
alert(stringValue.slice(-3))        //rld

//.substring(0)
alert(stringValue.substring(-3))    //hello world

//.substr(-3+length)
alert(stringValue.substr(-3))       //rld

//.slice(3,7)
alert(stringValue.slice(3,-4))      //lo w

//.substring(3,0)
alert(stringValue.substring(3,-4))  //hel

//.substr(3,0)
alert(stringValue.substr(3,-4))     //      //空字符串

3.字符串位置方法

str.indexOf("o",起始位置)
str.lastIndexOf("o",起始位置) 从字符串的末尾向前搜索子字符串。

4.trim()方法

删除前置和后缀的所有空格,返回结果,原始字符串不变。

5.大小写转换

通用
str.toLowerCase()
str.toUperCase()

针对地区
str.toLocaleLowerCase()
str.toLocaleUperCase()

一般来说,在不知道自己的代码将在那种语言环境中运行的情况下,还是使用针对地区的方法更稳妥一些。

6.字符串的模式匹配方法

模式匹配——正则表达式会专门写个博文,这里简单带过。

str.match(pattern)
str.search(pattern)
str.replace(pattern,str2)
str.split(pattern)

localeCompare()方法

实现所支持的地区(国家和语言)决定了这个方法的行为,比如美国以英语作为ECMAScript实现的标准语言,因此localeCompare()是区分大小写的,别的地区就不一定了。

var stringValue = "yellow"
console.log(stringValue.localeCompare("brick"))    //1
console.log(stringValue.localeCompare("yellow"))   //0
console.log(stringValue.localeCompare("zoo"))      //-1

"brick"在字母表中排在"yellow"之前,返回1
"zoo"在字母表中排在"yellow"之后,返回-1

HTML方法

尽量不要使用了,因为它们创建的标记通常无法表达语义。

单体内置对象

内置对象的定义:由ECMAScript实现提供的、不依赖与宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了。
意思就是说,开发人员不必显示地实例化内置对象,因为它们已经实例化了。

已介绍过的内置对象:Object、Array和String
还有两个单体内置对象:Global和Math

Global

待更新

Math

Math.abs(x) 返回数的绝对值。
Math.random() 返回 0 ~ 1 之间的随机数。
Math.max(x,y) 返回 x 和 y 中的最高值。
Math.min(x,y) 返回 x 和 y 中的最低值。
Math.ceil(x) 对数进行上舍入
Math.floor(x) 对数进行下舍入。
Math.round(x) 把数四舍五入为最接近的整数。

Math.sqrt(x) 返回数的平方根。
Math.exp(x) 返回 e 的指数。
Math.log(x) 返回数的自然对数(底为e)。
Math.pow(x,y) 返回 x 的 y 次幂。

Math.sin(x) 返回数的正弦。
Math.cos(x) 返回数的余弦。
Math.tan(x) 返回角的正切。

Math.acos(x) 返回数的反余弦值。
Math.asin(x) 返回数的反正弦值。
Math.atan(x) 以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值。
Math.atan2(y,x) 返回从 x 轴到点 (x,y) 的角度(介于 -PI/2 与 PI/2 弧度之间)。

Math.toSource() 返回该对象的源代码。

Math.valueOf() 返回 Math 对象的原始值。

小节

posted @ 2020-03-28 20:56  浮华而已-  阅读(399)  评论(3编辑  收藏  举报