3.6包装对象
包装对象:存取字符串、数字或布尔值的属性时创建的临时对象称做包装对象。
它是偶尔用来区分字符串值和字符串对象、数字和数值对象以及布尔值和布尔对象。
let s = 'hello world!'; //一个字符串
let world=s.substring(s.indexOf(" ")+1,s.length); //使用字符串的属性
疑问:字符串不是对象,为什么它会有属性呢?
只要引用了字符串s的属性,JavaScript就会将字符串值通过调用new String(s)的方式转换成对象,并被用来处理属性的引用。
一旦属性引用结束,这个新创建的对象就会销毁。
var s="test";//创建一个字符串
s.len=4;//给它设置一个属性 创建一个临时字符串对象 随即销毁这个对象
var t=s.len;//查询这个属性当运行这段代码时,t的值是undefined
说明修改只是发生在临时对象身上,而这个临时对象并未继续保留下来。
同字符串一样,数字和布尔值也具有各自的方法:通过Number()和Boolean()构造函数创建一个临时对象,方法的调用均是来自于这个临时对象。
注意:null和undefined没有包装对象:访问它们的属性会造成一个类型错误。
我们可通过String(),Number()或Boolean()构造函数来显式创建包装对象:
var s="test",n=1,b=true;//一个字符串、数字和布尔值
var S=new String(s);//一个字符串对象
var N=new Number(n);//一个数值对象
var B=new Boolean(b);//一个布尔对象
s == S //true
s === S //false
typeof s //string
typeof S // object
JavaScript会将包装对象转换成原始值,因此上段代码中的对象S、N和B常常——但不总是——表现的和值s、n和b一样。“==”等于运算符将原始值和其包装对象视为相等,但“===”全等运算符将它们视为不等。
通过typeof运算符可以看到原始值和其包装对象的不同。
3.7不可变的原始值和可变的对象引用
JavaScript的原始值(也称为原始类型或简单类型的值)
有哪些原始值?
**Undefined、****Null、****Boolean、****Number、****String、**Symbol(ES6引入)
原始值是不可更改的。
let s = 'hello';
s.toUpperCase();
console.log(s) => 'hello'
s调用了toUpperCase(),会打印出'HELLO',原始值s还是'hello'
let x = 5;
let y = x;
y = 10;
console.log(x); // 输出: 5
x的值是5,当我们将x赋值给y时,y实际上得到了5的一个副本。然后,当我们改变y的值为10时,这并不会影响到x的值。
let str = "hello";
str = str + " world";
console.log(str); // 输出: "hello world"
str + " world"并没有修改原始的str值,而是创建了一个新的字符串"hello world",并将其赋值给str。
JavaScript中的原始值是不可变的,这主要体现在它们是通过值传递的。任何对原始值的“修改”实际上都是创建了一个新的值,而原始值保持不变。
这种不可变性是通过JavaScript的内存管理和值传递机制实现的,而不是原始值本身具有某种内在的不可变性属性。
原始值的比较是值的比较,只有在它们的值相等时它们才相等。
对象
与原始值不同,对象是可变的。-----它们的值可以修改
let person = {
name: "Alice",
};
person.name = 'Betty';
person.age = 25;
let arr = [1, 2, 3]
arr[0] = 5
arr[3] = 6
对象的比较:
即使两个对象包含相同的属性和值,它们也是不相等的。
对象是引用类型,对象值是引用,对象的比较是引用的比较,当且仅当它们引用同一个基对象时,它们才相等。
3.8类型转换
3.8.3对象转换为原始值
所有对象(包括数组和函数)都被转换为 true;
假值(falsy values):
-
在 JavaScript 中,有几个特定的值被视为假值,它们包括:
false、0、-0、0n(BigInt 零)、""(空字符串)、null、undefined和NaN.Boolean(new Boolean(false)) ->true Boolean({}) ->true Boolean([]) ->true
假值 Boolean("") ->false Boolean(null) ->false
所有的对象继承了两个转换方法,
第一个是toString(),它的作用是返回一个反映这个对象的字符串。
例如,数组类(Array class)的toString()方法将每个数组元素转换为一个字符串,并在元素之间添加逗号后合并成结果字符串。函数类(Function class)的toString()方法返回这个函数的实现定义的表示方式。日期类(Date class)定义的toString()方法返回了一个可读的日期和时间字符串。RegExp类(RegExp class)定义的toString()方法将RegExp对象转换为表示正则表达式直接量的字符串:
({}).toString() '[object Object]'
[1,2,3].toString()//=>"1,2,3"
(function(x){f(x);}).toString()//=>"function(x){\n f(x);\n}"
/\d+/g.toString()
//=>"/\\d+/g"
new Date(2024,0,1).toString()//=>"Mon Jan 01 2024 00:00:00 GMT+0800 (中国标准时间)"
另一个转换对象的函数是valueOf()。这个方法的任务并未详细定义:如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值,因此默认的valueOf()方法简单地返回对象本身,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个默认方法,调用这些类型的实例的valueOf()方法只是简单返回对象本身。日期类定义的valueOf()方法会返回它的一个内部表示:1970年1月1日以来的毫秒数。
let a = {c:1} // {c: 1}
let d =
new Date(2024,0,1) // 1704038400000
4.1原始表达式
最简单的表达式是“原始表达式”(primary expression)。
原始表达式是表达式的最小单位——它们不再包含其他表达式。
JavaScript中的原始表达式包含常量或直接量、关键字和变量。
1.23//数字直接量
"hello"//字符串直接量
/pattern///正则表达式直接量
true//返回一个布尔值:真
false//返回一个布尔值:假
null//返回一个值:空
this//返回"当前"对象
i//返回变量i的值
sum//返回sum的值
undefined//undefined是全局变量,和null不同,它不是一个关键字
和其他关键字不同,this并不是一个常量,它在程序的不同地方返回的值也不相同。this关键字经常在面向对象编程中出现。在一个方法体内,this返回调用这个方法的对象。
4.2 对象和数组的初始化表达式
对象和数组初始化表达式实际上是一个新创建的对象和数组。这些初始化表达式有时称做“对象直接量”和“数组直接量”。然而和布尔直接量不同,它们不是原始表达式,因为它们所包含的成员或者元素都是子表达式。数组初始化表达式语法非常简单,我们以此开始。 数组初始化表达式是通过一对方括号和其内由逗号隔开的列表构成的。初始化的结果是一个新创建的数组。数组的元素是逗号分隔的表达式的值:
[]//一个空数组:[]内留空即表示该数组没有任何元素
[1+2,3+4]//拥有两个元素的数组,第一个是3,第二个是7
对象初始化表达式和数组初始化表达式非常类似,只是方括号被花括号代替,并且每个子表达式都包含一个属性名和一个冒号作为前缀
var p={x:2.3,y:-1.2};//一个拥有两个属性成员的对象
var q={};//一个空对象
q.x=2.3;q.y=-1.2;//q的属性成员和p的一样
4.3 函数定义表达式
函数定义表达式定义一个JavaScript函数。表达式的值是这个新定义的函数。从某种意义上讲,函数定义表达式可称为“函数直接量”。一个典型的函数定义表达式包含关键字function,跟随其后的是一对圆括号,括号内是一个以逗号分割的列表,列表含有0个或多个标识符(参数名),然后再跟随一个由花括号包裹的JavaScript代码段(函数体),例如:
//这个函数返回传入参数值的平方
var square=function(x){return x*x;}
4.4属性访问表达式
属性访问表达式运算得到一个对象属性或一个数组元素的值。JavaScript为属性访问定义了两种语法:
expression.identifer
expression[expression]
第一种写法是一个表达式后跟随一个句点和标识符。表达式指定对象,标识符则指定需要访问的属性的名称。
第二种写法是使用方括号,方括号内是另外一个表达式(这种方法适用于对象和数组)。第二个表达式指定要访问的属性的名称或者代表要访问数组元素的索引。
显然.identifier的写法更加简单,但需要注意的是,这种方式只适用于要访问的属性名称是合法的标识符,并且需要知道要访问的属性的名字。如果属性名称是一个保留字或者包含空格和标点符号,或是一个数字(对于数组来说),则必须使用方括号的写法。当属性名是通过运算得出的值而不是固定的值的时候,这时必须使用方括号写法
4.5 调用表达式
JavaScript中的调用表达式(invocation expression)是一种调用(或者执行)函数或方法的语法表示。它以一个函数表达式开始,这个函数表达式指代了要调用的函数。函数表达式后跟随一对圆括号,括号内是一个以逗号隔开的参数列表,参数可以有0个也可有多个,例如
f(0)//f是一个函数表达式;0是一个参数表达式
Math.max(x,y,z)//Math.max是一个函数;x,y和z是参数
a.sort()//a.sort是一个函数,它没有参数
当对调用表达式进行求值的时候,首先计算函数表达式,然后计算参数表达式,得到一组参数值。如果函数表达式的值不是一个可调用的对象,则抛出一个类型错误异常。
然后,实参的值被依次赋值给形参,这些形参是定义函数时指定的,接下来开始执行函数体。如果函数使用return语句给出一个返回值,那么这个返回值就是整个调用表达式的值。否则,调用表达式的值就是undefined。
4.6对象创建表达式
对象创建表达式(object creation expression)创建一个对象并调用一个函数(这个函数称做构造函数)初始化新对象的属性。对象创建表达式和函数调用表达式非常类似,只是对象创建表达式之前多了一个关键字new:
new Object()
new Point(2,3)
如果一个对象创建表达式不需要传入任何参数给构造函数的话,那么这对空圆括号是可以省略掉的:
new Object
new Date
4.7运算符概述
4.7.1概述
JavaScript中的运算符用于算术表达式、比较表达式、逻辑表达式、赋值表达式等。
大多数运算符都是由标点符号表示的,比如“+”和“=”。而另外一些运算符则是由关键字表示的,比如delete和instanceof。
4.7.7运算顺序
JavaScript总是严格按照从左至右的顺序来计算表达式。
例如,在表达式w=x+y*z中,将首先计算子表达式w,然后计算x、y和z,然后,y的值和z的值相乘,再加上x的值,最后将其赋值给表达式w所指代的变量或属性。
运算符的优先级和结合性规定了它们在复杂的表达式中的运算顺序,但并没有规定子表达式的计算过程中的运算顺序。
在任何一个表达式具有副作用而影响到其他表达式的时候,其求值顺序才会和看上去有所不同。如果表达式x中的一个变量自增1,这个变量在表达式z中使用,那么实际上是先计算出了x的值再计算z的值。
let a = 1
let b = (a++)+a
第一步:b
第二步:a++ --> 1
第三步:a --->2
第四步:b --->3
let c = 1
let d = ++c+c
1:d
2:++c --> 2
3:c --->2
4:d --->4
4.7.4运算符的副作用
“++”和“--”递增和递减运算符包含隐式的赋值。