秋招复习计划-JavaScript知识点11

  |  

前言

基本类型与引用类型转换中的装箱和拆箱操作,BOM属性对象方法,JS的map()和reduce()方法,JavaScript中的arguments


41



基本类型与引用类型转换中的装箱和拆箱操作

装箱:指将基本数据类型转换为对应的引用类型操作。装箱又分为隐式装箱显示装箱

拆箱:指将引用类型转换回基本的数据类型

隐式装箱

对于隐式装箱,我们先看个例子

1
2
let s1 = "test";
let s2 = s1.substring(2);

s1.substring(2)这一段代码执行的过程中,其实发生了很多步骤

  • 基于s1创建一个String类型的实例
  • 在实例中调用substring方法
  • 销毁这个实例

隐式装箱在读取一个基本类型值时,后台会创建一个该基本类型所对应的基本包装类型对象。在这个基本类型的对象上调用方法。这个基本类型的对象是临时的,它只存在于方法调用的那一行代码执行的瞬间,执行方法后立即本销毁。这也是在基本类型上添加属性和方法会不识别或报错的原因了。在添加属性和方法的代码执行的时候的确创建了一个基本包装类型的对象,所以添加的过程是没有问题的。但是添加完毕后,对象就被销毁了,所以后续的访问和调用就会报错。

衍生问题:

在研究隐式装箱的时候,我发现一个衍生的问题,就是数字在隐式装箱的时候会存在报错的现象。比如:

隐式装箱1

研究了一下,发现,这是JS引擎无法正确解析这里的点符号导致的。因为这里的点符号既可以理解为点运算符,是对对象方法或属性的访问。也可以理解为浮点数。

针对这个问题我们可以做出点调整,方法有很多

1
2
3
4
5
6
1..toString()    
//成功,运算结果"1" 解析: 第二个点被视为点运算符,前面的是浮点数。
1.0.toString()
//成功,运算结果"1" 解析: 第二个点被视为点运算符,前面的是浮点数。
1 .toString()
//成功,运算结果"1" 解析: 用空格和后面的.toString()隔开, 把前面的当成运算式处理

显示装箱

显示装箱就很好理解了,就是通过基本包装类型对象对基本类型进行装箱

1
let name = new String('test');

显示装箱的操作可以对new出来的对象进行属性和方法的添加了,因为通过new操作符创建的引用类型的实例,在执行流离开当前作用域之前一直保留在内存中。

拆箱操作

拆箱操作通常通过引用类型的valueOf()toString()方法来实现

不过需要注意valueOf和toStirng的区别

toString:将值转换为字符串形式并返回,不同类型的toString方法各有不同

valueOf:返回对象的原始值

1
2
3
4
5
6
7
8
9
let objNum = new Number(64);
let objStr = new String('64');
console.log(typeof objNum); // object
console.log(typeof objStr); // object
# 拆箱
console.log(typeof objNum.valueOf()); // number 基本的数字类型,想要的
console.log(typeof objNum.toString()); // string 基本的字符类型,不想要的
console.log(typeof objStr.valueOf()); // string 基本的数据类型,不想要的
console.log(typeof objStr.toString()); // string 基本的数据类型,想要的

还有一个值得注意的点,就是当一个对象同时定义了valueOf方法和toString方法,那么先执行哪个的问题。如果存在Symbol.toPrimitive()方法,这个是最优先的。然后对于不同的情况,valueOf和toString有不同的先后顺序

直接输出对象名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let obj = {
toString: function() {
console.log('调用了 obj.toString');
return 'toString';
},
valueOf: function() {
console.log('调用了 obj.valueOf')
return 'valueOf';
}
}

alert(obj);
// 调用了 obj.toString
// 弹出toString

执行tostring()

如果toString返回一个对象呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
toString: function() {
console.log('调用了 obj.toString');
return {};
},
valueOf: function() {
console.log('调用了 obj.valueOf')
return '1';
}
}

alert(obj);
// 调用了 obj.toString
// 调用了 obj.valueOf
// 弹出1

先toString,后valueOf

对象参与运算:

1
2
3
4
5
6
7
8
9
10
11
12
13
let obj = {
toString: function() {
console.log('调用了 obj.toString');
return 1;
},
valueOf: function() {
console.log('调用了 obj.valueOf')
return 2;
}
}
obj + 1;
//调用了 obj.valueOf
//3

执行valueOf()

而如果valueOf返回一个对象呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
valueOf: function() {
console.log('调用 valueOf');
return {};
},
toString: function() {
console.log('调用 toString');
return 10;
}
}

console.log(obj + 1);
//调用 valueOf
//调用 toString
//11

先执行valueOf,然后执行toString。

通过这两个例子我们就能知道了,虽然会先执行一个,但是如果先执行的那个返回的并不是基本类型的值得话,就会执行另一个。而如果两个都返回对象的话,我试了一下,会报错

方法名运算:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function test() {
let a = 1;
console.log(a);
}
test.valueOf = function() {
console.log('调用 valueOf方法');
return {}
}
test.toString = function() {
console.log('调用 toString方法')
return 3
}
test;
// 调用 valueOf 方法
// 调用 toString 方法
// 3

BOM属性对象方法

Window对象

Window方法

confirm:

创建一个需要用户确认的对话框,参数为提示信息

1
2
window.confirm('确认信息');
//点确认返回true

open:

打开一个新的浏览器窗口或查找一个已命名的窗口

1
window.open(URL, name, features, replace);
  • URL:一个可选字符串,声明了要在新窗口显示的文档的URL。如果省略了这个参数,或者它的值是空字符串,那么新窗口不会显示任何文档
  • name:一个可选的字符串,该字符串是一个由逗号分隔的特征列表,其中包含数字、字母和下划线,该字符声明了新窗口的名称。这个名称可以用作标记 \<a> 和\<form> 的属性 target 的值。如果该参数指定了一个已经存在的窗口,那么 open() 方法就不再创建一个新窗口,而只是返回对指定窗口的引用。在这种情况下,features 将被忽略。
  • featrues:一个可选的字符串,声明了新窗口要显示的标准浏览器的特征。如果省略该参数,新窗口将具有所有标准特征。在窗口特征这个表格中,我们对该字符串的格式进行了详细的说明。

  • replace:一个可选的布尔值。规定了装载到窗口的URL是在窗口的浏览历史中创建一个新条目,还是替换浏览器历史中的当前条目。支持下面的值:

    • true - URL替换浏览器历史中的当前条目
    • false - URL在浏览器历史中创建新的条目

close:

关闭当前窗口

1
window.close();

Window属性

closed:

页面是否关闭

1
2
3
4
let myWindow = window.open('http://www.baidu.com', 'baidu');
myWindow.closed; // false
myWindow.close();
myWindow.closed; // true

opener:

返回打开该窗口的窗口的引用

1
2
let myWindow = window.open('http://www.baidu.com', 'baidu');
myWindow.opener

document和history:

window和document以及history没有明确的所属关系。window只是存在一个document和history引用。因为window对象除了充当控制浏览器的对象外,还充当全局作用域。所以必须存在有document的引用。

appName属性:

一般浏览器除了ie(Internet explore)以外差不多打出来的appName几乎都是”Netscape”

1
navigator.appName; //"Netscape"

online属性:

指明系统是否处于脱机状态,也就是检查有没有网,没网返回false,有网返回true

userAgent属性:返回客户端的完整信息,可以判断机型、浏览器、手机端还是PC端

Screen对象

deviceXDPI:

返回显示屏幕的每英寸水平点(像素)数。返回的其实就是分辨率,而那个水平点数中的一点就是一像素,DPI是显示分辨率的一个比例值,DPI越高说明分辨率越高

availHeight:

屏幕的像素高度减去系统部件高度之后的值(只读),代表屏幕可用高度,单位为像素

height:

屏幕的像素高度

width:

屏幕的像素宽度

History对象

History对象属性

length:

在同一个标签页中,跳转了多少次。

例如,我们打开一个标签页,length为1,在标签页内访问百度,length为2。百度搜索一个内容,跳转到搜索结果页面,length为3.

History对象方法

back:

加载history列表中的前一个URL

forward:

加载history列表中的下一个URL

go:

加载history列表中的某个具体页面

Location对象

host属性:

设置或返回当前主机名和当前URL的端口号

hostname:

设置或返回当前URL的主机名

protocol:

返回或设置当前URL的协议

search:

返回或设置从问号开始的URL部分,及查询部分

hash:

设置或返回从井号开始的URL(锚)

JS中的map和reduce

map方法

定义: map方法创建一个新数组。数组内容为调用map方法的数组中的每个元素调用一个提供函数后的返回的结果。

例:

1
2
3
4
let array = [1,2,3,4,5];
const map1 = array.map(x => x * 2);
console.log(map1);
//[2,4,6,8,10]

需要注意的是,map()方法创建了一个新数组,但新数组并不是在遍历完原数组后才被复制,而是遍历一次就得到一个值。下面就是一个很好的例子

1
2
3
4
5
6
7
8
9
let array1 = [1, 4, 9, 16];

const map1 = array1.map(x => {
if (x == 4) {
return x * 2;
}
});
console.log(map1);
// [undefined, 8, undefined, undefined]

reduce方法

定义:为数组中的每一个元素一次执行回调函数,不包括数组中被删除或未被赋值的元素。

语法:

1
arr.reduce(callback, [initiaValue]);

回调函数接受四个参数:

  • previousValue: 上次调用回调函数返回的值,或者是提供的初始值(initiaValue)
  • currentValue:数组中当前被处理的元素
  • index:当前元素在数组中的索引
  • array:调用reduce的数组

initialValue参数作为第一次调用回调函数的第一个参数。

几个例子:

1
2
3
4
5
6
let arr = [1,2,3,4];
let sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
})
console.log(arr, sum);

reduce1

index从1开始,第一次的prev的值是数组的第一个值。数组长度是4,但是reduce函数循环3次。

1
2
3
4
5
6
let arr = [1,2,3,4];
let sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
}, 0);
console.log(arr, sum);

reduce2

这个例子index从0开始,因为我们传入了一个初始值0。

由这两个例子我们可以得出一个结论:

如果没有提供initialValue,reduce会从索引1的地方开始执行回调函数,跳过第一个索引。如果提供了,从索引0开始。

而如果调用reduce方法的数组为空,且没提供初始值,会引发报错。提供初始值就不会报错。

reduce的高级用法:

1.计算数组中每个元素出现的次数

1
2
3
4
5
6
7
8
9
10
11
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice', 'Bob'];
let nameNum = names.reduce((pre, cur) => {
if(cur in pre) {
pre[cur]++;
} else {
pre[cur] = 1;
}
return pre;
}, {});
console.log(nameNum);
//{Alice: 2, Bob: 2, Tiff: 1, Bruce: 1}

2.数组去重

1
2
3
4
5
6
7
8
9
10
let arr = [1,2,3,3,3,4,4,1]
let newArr = arr.reduce((pre, cur) => {
if(!pre.includes(cur)) {
return pre.concat(cur);
} else {
return pre;
}
}, []);
console.log(newArr);
//[1,2,3,4,1]

这个程序我在写的时候第一次考虑的不是用concat,而是push。当时我的想法就是把当前值压入之前返回的数组。但是运行的时候报错了。pre.includes不是一个方法。后来一想,push方法的返回值是压入的那个值,所以改用了concat。这也暴露我对一些函数的返回值记得不清楚的问题。

3.将二维数组转换为一维数组

1
2
3
4
5
6
let arr = [[0, 1], [2, 3], [4, 5]]
let newArr = arr.reduce((pre,cur)=>{
return pre.concat(cur)
},[])
console.log(newArr);
// [0, 1, 2, 3, 4, 5]

4.将多维数组转换为一维

1
2
3
4
5
6
let arr = [[0, 1], [2, 3], [4,[5,6,7]]]
const newArr = function(arr){
return arr.reduce((pre,cur)=>pre.concat(Array.isArray(cur)?newArr(cur):cur),[])
}
console.log(newArr(arr));
//[0, 1, 2, 3, 4, 5, 6, 7]

5.对象里的属性求和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let result = [
{
subject: 'math',
score: 10
},
{
subject: 'chinese',
score: 20
},
{
subject: 'english',
score: 30
}
];
let sum = result.reduce((pre, cur) => { return cur.score + pre; }, 0);
console.log(sum)
// 60

JS中的arguments

定义: arguments是所在函数的一个内置类数组对象。存放函数的调用时的参数。

在ES5的非严格模式下,arguments对象会反应出具名参数的变化

1
2
3
4
5
6
7
8
9
10
11
12
13
function mixArgs(first, second) {
console.log(first === arguments[0]);
console.log(second === arguments[1]) ;
first = "c";
second = "d";
console.log(first === arguments[0]);
console.log(second === arguments[1]);
}
//输出
true
true
true
true

在严格模式下,这种情况被消除了,具名参数的变化不会引起arguments的变化。

在ES6中,arguments对象跟ES5严格模式一样。而且在ES6引入参数默认值后,使用默认值的参数不会包含在arguments中。arguments对象反应了最初是的函数调用状态。

arguments对象还拥有一个callee属性,callee属性指向arguments所属的函数,因此我们可以不使用函数名来进行递归调用。

而且由于JS不存在函数重载,所以我们可以使用arguments来模拟函数重载。

文章目录
  1. 1. 基本类型与引用类型转换中的装箱和拆箱操作
    1. 1.1. 隐式装箱
    2. 1.2. 显示装箱
    3. 1.3. 拆箱操作
  2. 2. BOM属性对象方法
    1. 2.1. Window对象
      1. 2.1.1. Window方法
      2. 2.1.2. Window属性
    2. 2.2. Navigator对象
    3. 2.3. Screen对象
    4. 2.4. History对象
      1. 2.4.1. History对象属性
      2. 2.4.2. History对象方法
    5. 2.5. Location对象
  3. 3. JS中的map和reduce
    1. 3.1. map方法
    2. 3.2. reduce方法
  4. 4. JS中的arguments
|