首先,介绍标准库是什么东西,标准库就是 JS 内部已经给你写好的一些函数和方法,这些函数和对象很多都非常常用,我们直接使用即可,本章学习的就是 JS 提供的,或者说内置的对象。
标准库也称为标准 API(application programming interface)
这里所谓的标准库,就是 ES 标准,这是语言标准,我们知道,JS 不仅可以写在浏览器里面,还可以在 node 环境下执行,还有其他的环境也可以执行。现在学得标准库是通用的,也就是不管在什么环境下都可以使用的东西,任何一个 JS 环境都会实现这些标准库里的对象和函数。
本节介绍两个函数,分别是 Object 和 Function。
Object
Object 构造函数用于创建一个对象,这个构造函数如果传入 null 或者 undefined,可以创建一个空对象,如果传入是其他原始类型,则创建对应的包装类对象,如果传入的是一个对象,则直接返回该对象。
js
var obj1 = new Object(null);//空对象
var obj2 = new Object(2);//创建Number对象的2
var obj3 = [1,2,3];
var obj4 = new Object(obj3);//传入对象直接返回,此时相当于var obj4 = obj3
obj3[0] = 2;
console.log(obj4[0]);//输出2,说明obj3和obj4是同一个地址静态属性(方法)
js
//Object.keys();
//传入一个对象,返回该对象可以枚举的所有属性名的字符串数组
var obj = {
a: 123,
b: 345,
};
var arr = [3,4,5,8]
console.log(Object.keys(obj));//['a', 'b']
console.log(Object.keys(arr));//['0', '1', '2', '3']
//相当于获取这个对象所有属性名组成的数组
//Object.values();
//和上一个类似,这个函数可以返回所有可枚举的属性值所组成的数组
var obj = {
a: 123,
b: [1,2,3,4],
c:"string",
};
var arr = [3,4,5,8]
console.log(Object.values(obj));//[123, Array(4), 'string']
console.log(Object.values(arr));//[3, 4, 5, 8]
//Object.entries();
//和前面两个类似,这个函数得到的数组既包含属性名,又包含属性值
//该方法得到的是一个二维数组,即数组的每一项都是一个长度为2的数组,数组的第一项为属性名,第二项为属性值。实例属性(方法)
js
//Object.prototype.toString()
//返回一个对象字符串
//得到这个对象的字符串格式
//这个prototype先不用管,只看后面的toString
var obj = {
a:1,
b:2,
};
var str = obj.toString();
console.log(str);//[object Object] 相当于把对象转换为字符串
var num = 4;
console.log(num.toString());//"4"
//实际上,实例对象是可以重写覆盖的
obj.toString = function(){
return 1;
}
console.log(obj.toString());//1
//实际上,所有对象都有Object的实例对象和实例属性,至于原因将在进阶部分介绍,现在知道即可
console.log([1,2,3,4].toString());//1,2,3,4
//很显然,数组作为一个对象,预期的输出结果为[object Object],但实际却不是这样,这就是由于数组重写覆盖了这个方法。
console.log(Function.toString());//function Function() { [native code] }
//显然,函数也是一个对象,所以也具有toString方法,并且显然也重写了这个方法,会直接把这个函数的结构返回。
//Object.prototype.valueOf()
//得到某个对象的值,默认情况下,返回这个对象本身。
var obj = {
x:123,
y:234,
};
console.log(obj.valueOf());//{x: 123, y: 234}
console.log(obj.valueOf() === obj);//true
//显然,相当于就是把它自身返回了
//这里介绍一下这两个方法的实际用处,在JS中,当进行自动类型转换的时候,如果要对一个对象进行转换,例如将obj进行自动类型转换,实际上是自动执行了obj.valueOf(),如果得到的是一个原始值,则对这个原始值进行二次类型转换,如果得到的还是一个对象,则再调用toString方法转为字符串,然后再把这个字符串进行进一步转换。我们可以进行验证。
var obj = {
valueOf(){
return 1;
}
}
console.log(obj+1);//输出2
//对对象进行类型转换,先调用valueOf,得到的结果为1,这是一个原始类型,不需要再调用toString,直接返回
var obj2 = {
toString(){
return "1";
}
}
console.log(obj2+1);//"11"
//对obj进行类型转换,先进性valueOf函数,得到对象自身,由于还不是原始类型,所以二次调用toString方法,由于重写了toString方法,返回字符串1,所以进行字符串拼接。
//我们可以这样理解,当我们要把对象自动转换为原始类型的时候,实际上是调用valueOf方法,我们可以重写这个valueOf方法来自定义我这个对象转原始类型的规则。但是有一种可能就是使用valueOf方法之后,返回的还是一个对象,这样还是没有实现对象转原始类型的操作,此时就会强制调用toString方法强制转换为字符串。Function
在 JS 中,任何一个函数都是一个通过 Function 构造函数生成的对象。
arguments
arguments 在函数中使用,可以以数组的形式获取传入该函数的所有参数。非常好用。注意,这个引用获取到的是一个伪数组,也就是一个假的数组,是用对象模拟的数组,实际上,正儿八经的数组是通过 Array 构造函数创造的,而假的数组则并不是,虽然我们也可以遍历它,但是它缺少大量数组的实例方法和属性。还有一点要注意,在函数内部直接写 arguments 即可,函数名.arguments 写法是一个被弃用的写法,不信自己看:Function.prototype.arguments - JavaScript | MDN
js
function test(a,b){
console.log(a,b);
console.log(arguments);//直接写即可
//过时的写法:test.arguments
}
test(1,2);//可以查看结果arguments 厉害的地方是,arguments 可以获取传入的所有参数,尽管实参个数多于实参。
js
function test (a, b){
console.log (a, b);
console.log (arguments);//直接写即可
//过时的写法:test. arguments
}
test (1,2,3,4,5);//arguments可以获取到所有传入的参数arguments 更更更厉害的地方在于,它和参数列表一一映射,例如上例中,arguments[0] 就是参数 a,你把 arguments[0] 改了实际上就相当于改掉了参数 a,但这种映射是在最开始函数调用的时候发生的,也就是说如果我上面的例子中只传入了一个参数,那 arguments[0] 会和 a 进行映射,但 arguments[1] 不会和 b 映射,实际上,arguments 这个伪数组的长度,就取决于传入参数的个数,只有正确传入的参数才能被映射。
如果要获取函数形参的数量,使用 函数名.length 属性可以获取函数名。如果要获取函数名,可以使用 函数名.name 获取,name 属性有一点意思,var a = function b(){} 在这种情况下,把一个不是匿名函数的函数赋值给了另一个变量,此时访问 a.name 得到的是 b,这是唯一反直觉的地方。
接下来是 call 和 apply 方法,这两个方法的用处是一样的,就是运行函数,并且在此次运行中改变 this 指向
js
function test1(){
console.log(this);//输出this
}
var obj = {};
test1();//直接调用,this指向全局对象
obj.fn = test1;
obj.fn();//通过obj对象调用,this指向obj
test1.call(obj);//强制让this指向obj,此时输出obj
obj.fn.apply(window);//强制让this指向window,此时输出window
//可见,call和apply的功能实际上是一样,都是在本次调用的过程中改变this指向
//我们来看一个实例
var user1 = {
name:"ZYL",
age:"12",
}
var user2 = {
name:"YL",
age:"18",
fn(){
console.log(`我的名字是${this.name},我的年龄是${this.age}`);
}
}
//此时user1里面是没有这个fn方法的,但是我们可以使用user2的方法来访问user1的属性
user2.fn.call(user1);//此时输出:我的名字是ZYL,我的年龄是12。很显然,这里相当于是user1借用了user2的方法。
//这里对call和apply进行区分,他们唯一的区别在于参数的传递问题,对于call方法,参数格式为:call(this指向,参数1,参数2,...),对于apply方法,参数格式为:apply(this指向,[参数1,参数2,...]),除此之外,二者没有任何区别。
//call和apply有一个非常常见的应用场景,就是将一个伪数组转换为真数组
function test(a,b,c){
console.log(arguments);//这里是伪数组,这个伪数组是通过Object构造函数生成的,而不是Array
var newArray = [].slice.call(arguments);//这个空数组不用管他,我们主要是要借用数组的slice方法,所以这里就随便写一个空数组,这个slice方法可以得到一个数组的子数组
//slice中实际上用到了this,可以从当前数组中截取一段进行返回,此时返回的就是一个真实的数组
console.log(newArray);//真数组
}
test(2,3,4);
//前面的call和apply我们说严谨一点就是,立刻执行该函数,并且改变此次执行的this指向,那我现在有一个需求,我希望他不立刻执行函数,我就是要强制改变这个函数的this指向,让这个函数不管怎么去调用,this指向都给他绑定死,这个需求也可以做到,就是用bind方法
//Function.prototype.bind()
function test(){
console.log(this);
}
var obj = {};
var obj2 = {};
test();//window
test = test.bind(obj);
test();//obj
obj2.fn = test;
obj2.fn();//obj
//可以看到,bind函数会返回一个新函数,只要使用bind绑定过的函数,this指向会强制绑定,不管再以何种方式调用,this指向始终指向固定对象。
//bind第一个参数为要绑定的this指向,后面的参数依次就是函数传递的参数。