本篇内容
  • Function类型
  • Boolean、Number和String类型
  • Global、Math对象

Function类型

函数实际上是对象,每个函数都是Function类型的实例,具有属性和方法。函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

1.定义函数

  • 使用函数声明语法定义。如

    1
    2
    3
    function sum (num1, num2) {
    return num1 + num2
    }
  • 使用函数表达式定义(跟声明其他变量一样,函数末尾有一个分号)。如

    1
    2
    3
    var 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
9
console.log(test1());	// 'test1'
console.log(test2()); // TypeError

function test1 () {
return 'test1'
}
var test2 = function () {
return 'test2'
};

2.没有重载

重载,简单说,就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。

1
2
3
4
5
6
7
8
function addSomeNumber (num) {
return num + 100
}
function addSomeNumber (num) {
return num + 200
}

var result = addSomeNumber(100); // 300

3.函数内部属性

函数内部有两个特殊的对象:arguments和this。
其中arguments是一个类数组对象,包含着传入函数中的所有参数。arguments有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。如

1
2
3
4
5
6
7
function 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
12
window.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
9
function outer () {
inner();
}

function inner () {
console.log(inner.caller); // 等于arguments.callee.caller
}

outer(); // -> function outer

严格模式还有一个限制:不能为函数的caller属性赋值。

4.函数的属性和方法

每个函数都包含两个属性:length和prototype。
length属性表示函数希望接收的命名参数的个数。如

1
2
3
function 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
13
function 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
7
function 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
8
window.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
3
let addTen = num => num + 10;

addTen(10); // 12

当使用箭头函数创建普通对象时,你总是需要将对象包裹在小括号里(ES6中的规则是,紧随箭头的{被解析为块的开始,而不是对象的开始),如

1
2
3
var father = [1,34,53];
var childrens = father.map(puppy => {}); // 这样写不会返回任何东西
var childrens = father.map(puppy => ({})); //

在普通函数中,this指向它的直接调用者;如果找不到直接调用者,则是window;箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域,指的是定义它的对象。

基本包装类型

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

1
2
var str = 'some value';
var str2 = str.substring(2); // 后台创建String类型的一个实例,在此实例上调用substring方法,最后销毁此实例

等于

1
2
3
var 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
2
var obj = new Object('some text');
console.log(obj instanceof String); // true

*注意:使用new调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。如

1
2
3
4
5
6
var value = '25';
var number = Number(value);
console.log(number); // 25 (number)

var number2 = new Number(value);
console.log(number2); // 25 (object)

1.Boolean类型(不、要、用)

1
2
3
4
5
6
7
var falseObject = new Boolean(false);
var result = falseObject && true;
console.log(result); // true;

var falseValue = false;
var result2 = falseValue && true;
console.log(result2); // false

其中falseObject && true是对falseObject而不是它的值(false),因为是对象,所以转为true。

2.Number类型

toString()、toFixed()四舍五入保留小数点字符串(IE8及之前不能正确输入{(-0.94, -0.5][0.5, 0.94)}之间的值,返回0);
toExponential()返回以指数表示法(e)的字符串形式。接收一个参数,为输出结果小数点的位数。如

1
2
var num = 123456;
console.log(num.toExponential(2)); // "1.23e+5"

toPrecision()方法可以得到某个数值最合适的格式字符串。可能返回固定大小(fixed)格式,也可能返回指数(exponential)格式。该方法接收一个参数,表示数值的所有数字的位数(不包括指数部分)。如

1
2
3
4
var 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
2
var str = 'hello';
console.log(str.charAt(2)); // 'l'

charCodeAt()得到的是字符编码,如

1
2
var str = 'hello';
console.log(str.charCodeAt(2)); // '108'

ES5还支持了方括号加数字索引来访问字符串中的特定字符(早期浏览器则返回undefined),如

1
2
var str = 'hello';
console.log(str[2]); // 'l'

用于操作字符串的方法:concat()、slice()、substr()、substring()。
concat()方法用于将一或多个字符串拼接起来,返回拼接得到的字符串。可以接收任意多个参数。(实际上用’+’操作符更方便),如

1
2
3
4
5
6
var 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
8
var 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
8
var 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
3
var test = 'cat, bat, sat, fat';
var pos = test.search(/at/);
console.log(pos); // 1

为了简化字符串操作,ES提供了replace方法,这个方法接收两个参数,第一个参数可以是一个RegExp对象或者一个字符串(不会被转换成正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只替换第一个子字符串。要想替换所有子字符串,唯一的方法就是提供一个正则表达式,并且指定全局标志。如

1
2
3
var 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
12
function htmlEscape(text) {
return text.replace(/[<>"&]/g, function (match, pos, originalText) {
switch(match) {
case '<': return '&lt;';
case '>': return '&gt;';
case '&': return '&amp;';
case '"': return '&quot;'
}
})
}

console.log(htmlEscape('<p class="test">test & func</p>')); //'&lt;p class=&quot;test&quot;&gt;test &amp; func&lt;/p&gt;'

最后一个与模式匹配有关的方法是split(),这个方法可以基于指定分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是字符串也可以是一个RegExp对象(早期浏览器存在匹配差异),split()方法可以接收可选的第二个参数,用于指定数组的大小。如

1
2
3
4
var 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
    4
    var str = 'yellow';
    console.log(str.localeCompare('brick')); //1
    console.log(str.localeCompare('yellow')); // 0
    console.log(str.localeCompare('zoo')); // -1

fromCharCode()方法用于将一或多个字符编码转为一个字符串。如

1
2
var 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
3
var 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
5
var 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
4
var 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
2
var 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的属性和方法;

(完)