重温《JavaScript高级程序设计》—6.引用类型(Function、基本包装类型、单体内置对象)
Dec 10, 2017前端js本篇内容
- Function类型
- Boolean、Number和String类型
- Global、Math对象
Function类型
函数实际上是对象,每个函数都是Function类型的实例,具有属性和方法。函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
1.定义函数
使用函数声明语法定义。如
1
2
3function sum (num1, num2) {
return num1 + num2
}使用函数表达式定义(跟声明其他变量一样,函数末尾有一个分号)。如
1
2
3var sum = function (num1, num2) {
return num1 + num2
};使用Function构造函数(不推荐,会导致解析两次代码)。Function构造函数可以接收任意数量的参数,但最后一个参数始终被看成是函数体,前面的参数枚举了新函数的参数。如
1
var sum = new Function('num1', 'num2', 'return num1 + num2');
函数声明和函数表达式的区别:解析器在向执行环境中加载数据时,会先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。如1
2
3
4
5
6
7
8
9console.log(test1()); // 'test1'
console.log(test2()); // TypeError
function test1 () {
return 'test1'
}
var test2 = function () {
return 'test2'
};
2.没有重载
重载,简单说,就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。
1 | function addSomeNumber (num) { |
3.函数内部属性
函数内部有两个特殊的对象:arguments和this。
其中arguments是一个类数组对象,包含着传入函数中的所有参数。arguments有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。如1
2
3
4
5
6
7function factorial (num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1); // 跟factorial(num - 1) 差不多,但能消除耦合
}
}
this引用的是函数据以执行的环境对象,当在网页的全局作用域中调用函数时,this对象引用的就是window。如1
2
3
4
5
6
7
8
9
10
11
12window.color = 'red';
var o = {
color: 'blue'
};
function sayColor () {
console.log(this.color);
}
sayColor(); // 'red'
o.sayColor = sayColor;
o.sayColor(); // 'blue'
caller属性保存着调用当前函数的函数引用。如果是在全局作用域中调用当前函数,它的值为null(在严格模式下访问arguments.caller会导致错误,非严格模式为undefined)。如1
2
3
4
5
6
7
8
9function outer () {
inner();
}
function inner () {
console.log(inner.caller); // 等于arguments.callee.caller
}
outer(); // -> function outer
严格模式还有一个限制:不能为函数的caller属性赋值。
4.函数的属性和方法
每个函数都包含两个属性:length和prototype。
length属性表示函数希望接收的命名参数的个数。如1
2
3function sum (num1, num2) {}
console.log(sum.length); // 2
ES中的引用类型,prototype是保存它们所有实例方法的真正所在。ES5中prototype是不可枚举的。
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法都用于在特定的作用域中调用函数(实际上等于设置函数体的this对象的值,可以扩充函数赖以运行的作用域)。
其中apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中参数数组可以是Array的实例,也可以是arguments对象。如1
2
3
4
5
6
7
8
9
10
11
12
13function sum (num1, num2) {
return num1 + num2
}
function callSum1 (num1, num2) {
return sum.apply(this, arguments);
}
function callSum2 (num1, num2) {
return sum.apply(this, [num1, num2]);
}
callSum1(1, 2); //3
callSum2(1, 2); //3
在严格模式下,未指定环境对象而调用函数,则this值不会转型为window。除非明确把函数添加到某个对象或者调用apply()或call(),否则this值将是undefined。
call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。call()方法的第一个参数是this值,其余参数都直接传递给函数,即传递给函数的参数必须逐个列举出来。如1
2
3
4
5
6
7function sum (num1, num2) {
return num1 + num2
}
function callSum (num1, num2) {
return sum.call(this, num1, num2);
}
何时用call何时用apply:取决于你采取哪种给函数传递参数的方式最方便。如果你打算直接传入arguments对象,或者包含函数中先接收到的也是一个数组,那么使用apply()肯定更方法;否则使用call()可能更合适;不传参时两个都一样。
ES5还定义bind()方法,这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。如1
2
3
4
5
6
7
8window.color = 'red';
var o = { color: 'blue' };
function sayColor () {
console.log(this.color);
}
var oSayColor = sayColor.bind(o);
oSayColor(); // 'blue'
每个函数继承的toLocalString()和toString()方法始终返回函数的代码。
5.箭头函数(ES6)
语法:
单个参数时1
标识符 => 表达式
多个参数时1
(参数list) => 表达式
如1
2
3let addTen = num => num + 10;
addTen(10); // 12
当使用箭头函数创建普通对象时,你总是需要将对象包裹在小括号里(ES6中的规则是,紧随箭头的{被解析为块的开始,而不是对象的开始),如1
2
3var father = [1,34,53];
var childrens = father.map(puppy => {}); // 这样写不会返回任何东西
var childrens = father.map(puppy => ({})); //
在普通函数中,this指向它的直接调用者;如果找不到直接调用者,则是window;箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域,指的是定义它的对象。
基本包装类型
为了便于操作基本类型,ES还提供了3个特殊的引用类型:Boolean、Number和String。
这些类型与本章介绍的其他引用类型相似,但同时也具有与各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。如1
2var str = 'some value';
var str2 = str.substring(2); // 后台创建String类型的一个实例,在此实例上调用substring方法,最后销毁此实例
等于1
2
3var str = new String('some value');
var str2 = substring(2);
str = null;
以上操作同样适用于Boolean和Number类型对应的布尔值和数字值。
引用类型与基本包装类型的主要区别就是对象的生存期:使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在与一行代码的执行瞬间,然后立即被销毁。这也就是之前例子中对字符串创建属性无效的原因。如1
2
3
4
5
6
7
8
9
10
11
12
13// 自动创建
var str = 'sss';
str.name = 'sss';
console.log(str.name); // undefined
// 非自动创建
var str2 = new String('ddd');
str2.name = 'ddd';
console.log(str2.name); // ddd
console.log(typeof str2); // 'object'
console.log(str2 instanceof Object); // true
console.log(str2 instanceof String); // true
Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。如1
2var obj = new Object('some text');
console.log(obj instanceof String); // true
*注意:使用new调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。如1
2
3
4
5
6var value = '25';
var number = Number(value);
console.log(number); // 25 (number)
var number2 = new Number(value);
console.log(number2); // 25 (object)
1.Boolean类型(不、要、用)
1 | var falseObject = new Boolean(false); |
其中falseObject && true是对falseObject而不是它的值(false),因为是对象,所以转为true。
2.Number类型
toString()、toFixed()四舍五入保留小数点字符串(IE8及之前不能正确输入{(-0.94, -0.5][0.5, 0.94)}之间的值,返回0);
toExponential()返回以指数表示法(e)的字符串形式。接收一个参数,为输出结果小数点的位数。如1
2var num = 123456;
console.log(num.toExponential(2)); // "1.23e+5"
toPrecision()方法可以得到某个数值最合适的格式字符串。可能返回固定大小(fixed)格式,也可能返回指数(exponential)格式。该方法接收一个参数,表示数值的所有数字的位数(不包括指数部分)。如1
2
3
4var num = 99;
console.log(num.toPrecision(1)); // '1e+2'
console.log(num.toPrecision(2)); // '99'
console.log(num.toPrecision(3)); // '99.0'
toPrecision()方法会根据要处理的数值决定到底是调用toFixed()还是调用toExponential()。
3.String类型
用于访问字符串中特定字符的方法:charAt()和charCodeAt()。两个方法都接收一个参数,即基于0的字符位置。
charAt()方法以单字符字符串的形式返回给定位置的那个字符。如1
2var str = 'hello';
console.log(str.charAt(2)); // 'l'
charCodeAt()得到的是字符编码,如1
2var str = 'hello';
console.log(str.charCodeAt(2)); // '108'
ES5还支持了方括号加数字索引来访问字符串中的特定字符(早期浏览器则返回undefined),如1
2var str = 'hello';
console.log(str[2]); // 'l'
用于操作字符串的方法:concat()、slice()、substr()、substring()。
concat()方法用于将一或多个字符串拼接起来,返回拼接得到的字符串。可以接收任意多个参数。(实际上用’+’操作符更方便),如1
2
3
4
5
6var str = 'hello';
var str2 = str.concat(' ', 'world', '!');
var str3 = str + ' ' + 'world' + '!';
console.log(str2); // 'hello world!'
console.log(str3); // 'hello world!'
slice()、substr()和substring()接收一或二个参数。第一个参数指定字符串的起始位置,第二个参数表示字符串到哪里结束(substr()第二个参数指定的是返回的字符个数)。如1
2
3
4
5
6
7
8var str = 'hello world';
var str2 = str.slice(3); // "lo world"
var str3 = str.substr(3); // "lo world"
var str4 = str.substring(3); // "lo world"
var str5 = str.slice(3, 5); // "lo"
var str6 = str.substr(3, 5); // "lo wo"
var str7 = str.substring(3, 5); // "lo"
在传递给这些方法的参数是负值的情况下,slice()方法会将传入的负值与字符串的长度相加;substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为0;substring()方法会把所有负值参数都转为0。如1
2
3
4
5
6
7
8var str = 'hello world';
var str2 = str.slice(-2); // 'ld'
var str3 = str.substr(-2); // 'ld'
var str4 = str.substring(-2); // 'hello world'
var str5 = str.slice(1, -3); // 'ello wo'
var str6 = str.substr(1, -3); // ''
var str7 = str.substring(1, -3); // 'h'
用于在字符串中查找字符串的方法:indexOf()和lastIndexOf()。两个方法都是从一个字符串中搜索指定的子字符串,然后返回子字符串的位置,没找到返回-1。
ES5为所有字符串定义了trim()方法,这个方法会创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。(部分浏览器还有trimLeft()和trimRight()方法)。
用于大小写转换的方法:toLowerCase()、toLocaleLowerCase()、toUpperCase()、toLocaleUpperCase()。toLocaleLowerCase()和toLocaleUpperCase()方法是针对特定地区的实现。
用于字符串匹配的方法:match()、search()。
match()方法本质与RegExp的exec()方法相同。
search()方法返回字符串中第一个匹配项的索引,没有匹配项返回-1。如1
2
3var test = 'cat, bat, sat, fat';
var pos = test.search(/at/);
console.log(pos); // 1
为了简化字符串操作,ES提供了replace方法,这个方法接收两个参数,第一个参数可以是一个RegExp对象或者一个字符串(不会被转换成正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只替换第一个子字符串。要想替换所有子字符串,唯一的方法就是提供一个正则表达式,并且指定全局标志。如1
2
3var test = 'cat, bat, sat, fat';
var res = test.replace('at', ''); // 'c, bat, sat, fat'
var res2 = test.replace(/at/g, ''); // 'c, b, s, f'
replace()方法的第二个参数是函数时,在只有一个匹配项(即与模式匹配的字符串)的情况下,会向这个函数传递3个参数:模式的匹配项,模式匹配项在字符串中的位置和原始字符串。在正则表达式中定义了多个捕获组的情况下,传递给函数的一次是模式的匹配项、第一个捕获组的匹配项、第二个捕获组的匹配项。。。,但最后两个参数仍然分别是匹配项在字符串中的位置和原始字符串。这个函数应该返回一个字符串,表示应该被替换的匹配项使用函数replace()方法的第二个参数可以实现更加精细的替换操作。如1
2
3
4
5
6
7
8
9
10
11
12function htmlEscape(text) {
return text.replace(/[<>"&]/g, function (match, pos, originalText) {
switch(match) {
case '<': return '<';
case '>': return '>';
case '&': return '&';
case '"': return '"'
}
})
}
console.log(htmlEscape('<p class="test">test & func</p>')); //'<p class="test">test & func</p>'
最后一个与模式匹配有关的方法是split(),这个方法可以基于指定分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是字符串也可以是一个RegExp对象(早期浏览器存在匹配差异),split()方法可以接收可选的第二个参数,用于指定数组的大小。如1
2
3
4var test = 'red, yellow, blue, green';
var res1 = test.split(','); // ["red", " yellow", " blue", " green"]
var res2 = test.split(/[^e]+/); // ["", "e", "e", "e", "ee", ""]
var res3 = test.split(' ', 2); // ["red,", "yellow,"]
localeCompare()方法用于比较两个字符串,并且返回:
- 如果字符串在字母表中应该排在字符串参数之前,返回一个负数;
- 如果字符串等于字符串参数,返回0;
- 如果字符串在字母表中应该排在字符串参数之后,返回一个正数;
如1
2
3
4var str = 'yellow';
console.log(str.localeCompare('brick')); //1
console.log(str.localeCompare('yellow')); // 0
console.log(str.localeCompare('zoo')); // -1
fromCharCode()方法用于将一或多个字符编码转为一个字符串。如1
2var str = String.fromCharCode(104, 101, 108, 108, 111);
console.log(str); // 'hello'
单体内置对象
内置对象的定义:有ES实现提供的、不依赖于宿主环境的对象,这些对象在ES程序执行之前就已经存在了。如Object、Array、String。
ES还定义了两个单体内置对象:Global和Math。
1.Global对象。
不属于任何其他对象的属性和方法,最终都是它的属性和方法。所有在全局作用域中定义的属性和函数,都是Global对象的属性。例如之前的isNaN()、parseInt()等,实际上都是Global对象的方法。
URI编码方法:encodeURI()和encodeURIComponent()方法可以对URI(Uniform Resource Identifier,通用资源标识符)进行编码。有效的URL中不能包含某些字符,如空格。
encodeURI()用于整个URI,encodeURIComponent()主要用于对URI中某一段进行编码。它们的区别是:encodeURI()不会对本身属于URI的特殊字符进行编码,例如冒号、正斜杠、问号和井字号;而encodeURIComponet()则会对它发现的任何非标准字符进行编码。如1
2
3var uri = 'https://michealwayne.github.io?hello test';
console.log(encodeURI(uri)); // 'https://michealwayne.github.io?hello%20test'
console.log(encodeURIComponent(uri)); // 'https%3A%2F%2Fmichealwayne.github.io%3Fhello%20test'
encodeURI()和encodeURIComponent()方法对应的两个方法是decodeURI()和decodeURIComponent()。
decodeURI()只能对encodeURI()替换的字符进行解码。
eval()方法只接收一个参数,即要执行的ES字符串。解析器发现代码中调用eval()方法时,它会将传入的参数当作实际的ES语句来解析,然后把执行结果插入到原位置。通过eval()执行的代码被认为是包含该次调用的执行环境的一部分,因此被执行的代码具有与该执行环境相同的作用域链。
在eval()中创建的任何变量或函数都不会被提升,只在eval()执行时创建。
严格模式下,在外部访问不到eval()中创建的任何变量或函数,为eval赋值也会导致错误。
Global对象的属性
属性 | 说明 |
---|---|
undefined | 特殊值undefined |
NaN | 特殊值NaN |
Infinity | 特殊值Infinity |
Object | 构造函数Object |
Array | 构造函数Array |
Function | 构造函数Function |
Boolean | 构造函数Boolean |
String | 构造函数String |
Number | 构造函数Number |
Date | 构造函数Date |
RegExp | 构造函数RegExp |
Error | 构造函数Error |
EvalError | 构造函数EvalError |
RangeError | 构造函数RangeError |
ReferenceError | 构造函数ReferenceError |
SyntaxError | 构造函数SyntaxError |
TypeError | 构造函数TypeError |
URIError | 构造函数URIError |
在全局作用域中声明的所有变量和函数,都成为了window对象的属性。
2.Math对象
Math对象的属性
属性 | 说明 |
---|---|
Math.E | 自然对数的底数,即常量e |
Math.LN10 | 10的自然对数 |
Math.LN2 | 2的自然对数 |
Math.LOG2E | 以2为底e的对数 |
Math.LOG10E | 以10为底的对数 |
Math.PI | π的值 |
Math.SQRT1_2 | 1/2的平方根 |
Math.SQRT2 | 2的平方根 |
max()和min()方法用于确定一组数值中的最大值和最小值。可以接收任意多个数值参数。如1
2
3
4
5var max = Math.max(3, 54, 32, 16); // 54
var min = Math.min(1, 2); // 1
var arr = [123, 1, 2];
var max2 = Math.max.apply(Math, values); // 123
ceil()方法向上取整,floor()方法向下取整、round()四舍五入。如1
2
3
4var num = 0.5;
console.log(Math.ceil(num)); // 1
console.log(Math.floor(num)); // 0
console.log(Math.round(num)); // 1
random()返回大于0小于1的一个随机数。如1
2var num = Math.random(); // 0.028345675226665312
var num2 = Math.random(); // 0.9463474415017943
其他方法
方法 | 说明 |
---|---|
Math.abs(num) | 返回num的绝对值 |
Math.exp(num) | 返回Math.E的num次幂 |
Math.log(num) | 返回num的自然对数 |
Math.pow(num, power) | 返回num的power次幂 |
Math.sqrt(num) | 返回num的平方根 |
Math.acos(x) | 返回x的反余弦值 |
Math.asin(x) | 返回x的反正弦值 |
Math.atan(x) | 返回x的反正切值 |
Math.atan2(y, x) | 返回y/x的反正切值 |
Math.cos(x) | 返回x的余弦值 |
Math.sin(x) | 返回x的正弦值 |
Math.tan(x) | 返回x的正切值 |
ES6对引用类型增添了很多丰富的属性和方法,此篇不过多介绍
温习:
- Function类型的定义、arguments、this、callee、caller、length和prototype等属性方法;
- 基本包装类型Boolean、String、Number的”特殊性”及方法;
- Global和Math的属性和方法;
(完)
Author
My name is Micheal Wayne and this is my blog.
I am a front-end software engineer.
Contact: michealwayne@163.com