Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

引用类型 #4

Open
K-Kevin opened this issue Sep 21, 2019 · 0 comments
Open

引用类型 #4

K-Kevin opened this issue Sep 21, 2019 · 0 comments

Comments

@K-Kevin
Copy link
Owner

K-Kevin commented Sep 21, 2019

引用类型的值(对象)是引用类型的一个实例。在 ES 中,引用类型是一种数据结构,用于将数据和功能组织在一起。通常被称为类,但是实际上这种称呼并不妥当。尽管 ES 从技术上讲是一门面向对象语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构。引用类型有时候也被称为对象定义,因为他们描述的是一类对象所具有的属性和方法。

PS:本章节摘自于JS宝典,其中有些描述也已过时,甚至在严格模式下不能使用,或者已经废弃的,但是在了解 ES 的历史背景的情况下,的确可以加深对 JS 的理解。js 是一门非常灵活的语言,所以我甚至以为第一门开发语言就学习 js 是不利于理解面向对象程序设计的,事实上看了以后的确如此。甚至很多方法的设计为了灵活都非常简化,不利于去理解他的作用与原理。

就像前面说的,对象是某个特定引用类型的实例。新对象是使用 new 操作符跟着一个构造函数创建的。而构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。

1. Object 类型

到目前为止,我们看到的大多数引用类型都是 Object 类型的实例;而且,Object 也是 ES 中使用最多的一个类型。

创建 Object 实例的方式有两种:

  • 使用 new 操作符后跟 Object 构造函数

    var person = new Object();
    person.name = "Nicholas";
    person.age = 29;
  • 使用字面量表达式

在通过字面量定义对象时,实际上不会调用 Object 构造函数(数组同理)。

var person = {
    name : "Nicholas",
    age : 29
};

一般来说,访问对象属性时都是用的点表达式,这也是很多面向对象语言中通用的语法。不过当我们需要使用通过变量来访问属性时需要使用方括号表达式来访问属性,其他情况推荐使用点语法。

Symbol 值作为对象属性名时,不能使用点语法!

var propertyName = "xxx";
alert(person[propertyName]);

2. Array 类型

除了 Object 之外,Array 就是我们最常用的类型了。和其他语言一样,也是有序列表,但是区别是 ES 数组的每一项都可以保存任何类型的数据。

关于 length 有个特点,它不是只读的,因此可以通过这个属性,可以从数组的末尾移除或向数组中添加新项:

//将长度设置为 2 会移除最后一项
var colors = ["red", "blue", "green"];    //creates an array with three strings
colors.length = 2;
alert(colors[2]);        //undefined
//将 length 设置为大于数组长度的值,则新增的每一个都是 undefined
var colors = ["red", "blue", "green"];    //creates an array with three strings
colors.length = 4;
alert(colors[3]);        //undefined

我们还可以利用 length 属性在数组末尾添加新项:

ar colors = ["red", "blue", "green"];    //creates an array with three strings
colors[colors.length] = "black";          //add a color
colors[colors.length] = "brown";          //add another color

alert(colors.length);    //5
alert(colors[3]);        //black
alert(colors[4]);        //brown

2.1 检测数组

if value instanceof Array {
    //todo
}

instanceof 操作符的问题在于,它假定只有一个全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的 Array 构造函数。为此,ES5 新增了 Array.isArray() 方法

2.2 转换方法

所有对象都有 toLocaleString()、toString()、valueOf() 方法。

valueOf() 返回数组本身,toString() 返回逗号分隔的字符串。

另外,toLocaleString() 方法一般与上面两个方法相同的值,但也不总是如此。

var person1 = {
    toLocaleString : function () {
        return "Nikolaos";
    },

    toString : function() {
        return "Nicholas";
    }
};

var person2 = {
    toLocaleString : function () {
        return "Grigorios";
    },

    toString : function() {
        return "Greg";
    }
};

var people = [person1, person2];
alert(people);                      //Nicholas,Greg
alert(people.toString());           //Nicholas,Greg
alert(people.toLocaleString());     //Nikolaos,Grigorios

使用 join 方法则可以使用不同的分隔符来得到这个字符串。

var colors = ["red", "green", "blue"];
alert(colors.join(","));      //red,green,blue
alert(colors.join("||"));     //red||green||blue

2.3 栈方法

栈是 LIFO 的结构,ES 专门提供了 push 和 pop 方法

push() 可以接收任意数量的参数,把他们逐个添加到数组末尾,并返回数组的长度;

pop() 方法则从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项;

var colors = new Array();                      //create an array
var count = colors.push("red", "green");       //push two items
alert(count);  //2

count = colors.push("black");                  //push another item on
alert(count);  //3

var item = colors.pop();                       //get the last item
alert(item);   //"black"
alert(colors.length);  //2

2.4 队列方法

队列是 FIFO 的结构,shift()、unshift() 就是 ES 提供的方法

shift() 方法能够移除数组的第一个项并返回该项,同时将数组长度减 1。结合使用 shift() 和 push() 方法,可以像使用队列一样使用数组。

var colors = new Array();                      //create an array
var count = colors.push("red", "green");       //push two items
alert(count);  //2

count = colors.push("black");                  //push another item on
alert(count);  //3

var item = colors.shift();                     //get the first item
alert(item);   //"red"
alert(colors.length);  //2

unshift() 和上述相反,可以从相反的方向来模拟队列,即在数组的最前端添加项,从数组末端移除项:

var colors = new Array();                      //create an array
var count = colors.unshift("red", "green");    //push two items
alert(count);  //2

count = colors.unshift("black");               //push another item on
alert(count);  //3

var item = colors.pop();                     //get the first item
alert(item);   //"green"
alert(colors.length);  //2

2.5 重排序

  • reserse() 会反转数组项的顺序
  • sort() 默认按升序,也可以接收一个比较函数作为参数

这里说下 sort ,先看下以下代码:

var values = [0, 1, 5, 10, 15];
values.sort();
alert(values);    //0,1,10,15,5

比较的结果显然不是我们想要的,这是为什么呢?

sort 方法会调用每个数组项的 toString 转型方法,然后比较得到的字符串。即使数组的每一项都是数值,sort 方法比较的也是字符串,下面我们进行传入比较函数来解决这个问题:

function compare(value1, value2) {
    if (value1 < value2) {
        return -1;
    } else if (value1 > value2) {
        return 1;
    } else {
        return 0;
    }
}

var values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values);    //0,1,5,10,15

2.6 操作方法

concat() --不会改变原始数组

concat() 可以基于当前数组中的所有项创建一个新数组。具体来说,就是创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾。可以传递多个数组。

var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);

alert(colors);     //red,green,blue        
alert(colors2);    //red,green,blue,yellow,black,brown
slice()--不会改变原始数组

slice() 方法能够基于当前数组中的一个或多个项创建一个新数组。接收一个或两个参数,即要返回项的起始和结束位置。只有一个参数的情况下,默认表示从该参数指定到结尾的所有项。如果有两个,则表示之间的项,但不包含结束位置的项。

var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);

alert(colors2);   //green,blue,yellow,purple
alert(colors3);   //green,blue,yellow

如果参数中有一个负数,则用数组长度加上该数来确定相应的位置。例如一个长度为5的数组 slice(-2, -1) 等同于 slice(3, 4). 如果结束位置小于起始位置,则返回空数组。

splice()--会改变原始数组
  • 删除:可以删除任意数量的项,只需指定 2个参数:要删除的第一项的位置和要删除的项数。
  • 插入:可以向指定位置插入任意数量的项,3个参数:起始位置、0(要删除的项数)和插入的项。如果要插入多个项,可以再传入第四、第五,以至于更多参数。
  • 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,也是3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必和删除的相等。

splice() 方法始终都会返回一个数组,该数组中包含从原始数组中删除的项。(如果没删除,则返回空数组)

var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1);              //remove the first item
alert(colors);     //green,blue
alert(removed);    //red - one item array

removed = colors.splice(1, 0, "yellow", "orange");  //insert two items at position 1
alert(colors);     //green,yellow,orange,blue
alert(removed);    //empty array

removed = colors.splice(1, 1, "red", "purple");    //insert two values, remove one
alert(colors);     //green,red,purple,orange,blue
alert(removed);    //yellow - one item array

2.7 位置方法

indexOf() 和 lastIndexOf() 都接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。都要返回要查找的项在数组的位置,或者在没找到的情况下返回 -1。比较的时候也是全等操作符。

  • indexOf():从开头开始向后查找
  • lastIndexOf():从末尾开始向前查找
alert(numbers.indexOf(4));        //3
alert(numbers.lastIndexOf(4));    //5

alert(numbers.indexOf(4, 4));     //5
alert(numbers.lastIndexOf(4, 4)); //3       

var person = { name: "Nicholas" };
var people = [{ name: "Nicholas" }];
var morePeople = [person];

alert(people.indexOf(person));     //-1
alert(morePeople.indexOf(person)); //0

补充:之前看到过别人发过一个面试题

有两个数组['a', 'b', 'c', 'd'],['a', 'd', 'c', 'e'],数组元素都是 String,且没有重复,如何判断两个数组中不同元素的集合(去交集)?(代码层面上不超过1次循环,数组的 filter、forEach、reduce、map、every、some 均属于循环)

var result = ['a', 'b', 'c', 'd'].concat(['a', 'd', 'c', 'e']).filter(function(value, index, arr) {
    return arr.indexOf(value) === arr.lastIndexOf(value);
});
console.log(result);

2.8 迭代方法

ES5 定义了5个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象--影响 this 的值。传入的函数会接收三个参数:value、index、arr(本身)。

以下 5 个迭代方法都不会修改数组中的包含的值。

  • every():对数组的每一项运行指定函数,如果每一个都返回 true,则返回 true。
  • filter():对数组的每一项运行指定函数,返回该函数会返回 true 的项组成的数组。
  • forEach():对数组的每一项运行指定函数。无返回值。
  • map():对数组的每一项运行指定函数,返回每次函数调用的结果组成的数组。
  • some():对数组的每一项运行指定函数,如果该函数对任一项返回 true,则返回 true。
every some

every 和 some 相似,用于查询数组中的项是否满足某个条件。

对于 every 来说,每一个都返回 true 才能返回 true。

而 some 只要有某一个返回 true,就会返回 true。

var numbers = [1,2,3,4,5,4,3,2,1];
        
var everyResult = numbers.every(function(item, index, array){
    return (item > 2);
});

alert(everyResult);       //false

var someResult = numbers.some(function(item, index, array){
    return (item > 2);
});

alert(someResult);       //true
filter

filter 利用指定的函数确定是否在返回的数组中包含某一项。例如要返回一个都大于 2 的数组:

var numbers = [1,2,3,4,5,4,3,2,1];
        
var filterResult = numbers.filter(function(item, index, array){
    return (item > 2);
});

alert(filterResult);   //[3,4,5,4,3]
map

map 则是都在原始数组中的对应值上进行运行函数的结果组成返回值

var numbers = [1,2,3,4,5,4,3,2,1];
        
var mapResult = numbers.map(function(item, index, array){
    return item * 2;
});

alert(mapResult);   //[2,4,6,8,10,8,6,4,2]

2.9 归并方法

reduce()和 reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。

reduce()方法从数组的第一项开始,逐个遍历到最后。

而 reduceRight()则从数组的最后一项开始,向前遍历到第一项。

都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。而函数则接收 4个参数:前一个值、当前值、索引、数组对象。这个函数的返回值会作为第一个参数自动传给下一项。

reduce()、reduceRight()可以执行求和操作:

var values = [1,2,3,4,5];
var sum = values.reduce(function(prev, cur, index, array){
    return prev + cur;
});
alert(sum);

3.Date 类型

UTC balabala 好像没啥好说的

4. RegExp 类型

正则我是整体都不是很了解,后面也许、可能会单独讲吧...

5. Function 类型

首先!函数实际上是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有方法和属性。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。函数通常使用函数声明语法定义的:

//函数声明
function sum(num1, num2) {
    return num1 + num2;
}
//这与下面使用函数表达式定义函数的方式几乎相差无几。
var sum = function (num1, num2) {
    return num1 + num2;
};

以上代码看到,function 关键字后面没有函数名。这是因为再使用函数表达式定义函数时,没有必要使用函数名---通过变量 sum 即可引用函数。

另外还有一种定义函数的方式是使用 Function 构造函数。Function 构造函数可以接收任意数量的参数,但最后一个参数始终都被看成是函数体,而前面的参数则枚举出了新函数的参数。

var sum = new Function('num1', 'num2', 'return num1 + num2');//不推荐

从技术上讲,这是一个函数表达式。但是不推荐这种用法。因为会导致两次解析(一次常规解析 ES 代码,第二次是解析传入构造函数中的字符串,从而影响性能)。

这种语法对于理解“函数是对象,函数名是指针”的概念是非常直观的。

从下面这段代码我们来加深下函数是对象的概念:

function sum(num1, num2){
    return num1 + num2;
}        
alert(sum(10,10));    //20

var anotherSum = sum;        
alert(anotherSum(10,10));  //20

sum = null;        
alert(anotherSum(10,10));  //20

不使用圆括号的函数名是访问函数指针,并非调用函数。

5.1 没有重载

将函数名想象为指针,也有助于理解为什么 ES 中没有函数重载的概念。

function addSomeNumber(num){
    return num + 100;
}        

function addSomeNumber(num){
    return num + 200;
}

var result  = addSomeNumber(100); //300

以上例子声明了两个同名函数,而结果则是后面的覆盖了前面的函数。其实上面代码和下面的没有什么区别,这样可能就理解了为什么被覆盖了,也理解了为什么函数没有重载的概念:

var addSomeNumber = function (num){
    return num + 100;
};        

addSomeNumber = function (num){
    return num + 200;
};

var result  = addSomeNumber(100); //300

5.2 函数表达式与函数声明

通常我们在使用函数表达式与函数声明并无什么区别,而实际上,解析器在向执行环境中加载数据时,对函数声明和表达式并非相同。解析器会率先读取到函数声明,并使其在执行任何代码之前可用;至于函数表达式,则必须等解析器执行到他所在的代码行,才会真的被解释执行。

alert(sum(10,10));    //20

function sum(num1, num2){
    return num1 + num2;
}  

上面代码运行OK,因为在代码执行前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。对代码求值时,JavaScript 引擎在第一遍会声明函数并将他们放到源码树的顶部。所以即使声明函数的代码再调用他的代码后,JavaScript 引擎也能把函数声明提升到顶部。

像下面的这个代码就会导致错误:

alert(sum(10,10));    //causes an error

var sum = function(num1, num2){
    return num1 + num2;
}; 

之所以会报错,原因在于函数位于一个初始化语句中,而不是一个函数声明。

5.3 作为值的函数

可以作为参数传递给另一个函数,如果你能理解函数就是对象,函数名就是指针,那么你就知道函数名本身就是变量,当然也可以作为值来使用。

5.4 函数内部属性

这里要说的是在函数内部的两个特殊对象:arguments 和 this。

1.arguments

arguments 是一个类数组对象,包含着传入函数中的所有属性。主要用途是保存函数参数,这个对象还有一个名叫 callee 的属性,是一个指针,指向拥有这个 arguments 对象的函数。

callee 包括 caller 都是为了弄清原理,实际上是不建议使用的,在严格模式下问题颇多,我们只是探究其原理!

function factorial(num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * factorial(num - 1);
    }
}

这是一个阶乘递归算法,递归的问题在于耦合,修改函数名称就要修改两处。为了消除这个耦合,就可以像下面这样使用 arguments.callee

function factorial(num){
    if (num <= 1) {
        return 1;
    } else {
        return num * arguments.callee(num-1)
    }
}

var trueFactorial = factorial;

factorial = function(){
    return 0;
};

alert(trueFactorial(5));   //120
alert(factorial(5));       //0
2.this

另一个特殊对象是 this,其行为与 Java 和 C# 中的 this 大致类似。换句话说,this 引用的是函数执行的环境对象——或者也可以说是 this值(当在网页的全局作用域中调用函数时,this 对象引用的就是 window)。

window.color = "red";
var o = { color: "blue" };

//定义在全局作用域中,引用了 this 对象
function sayColor(){
    alert(this.color);
}

sayColor();     //red

o.sayColor = sayColor;
o.sayColor();   //blue	

要牢记,函数的名字仅仅是一个包含指针的变量而已。全局的 sayColor()与o.sayColor()指向的是同一个函数。

caller

caller 属性保存着调用当前函数的引用,如果是在全局作用域中调用当前函数,它的值为 null。

function outer(){
    inner();
}

function inner(){
    alert(inner.caller);
}

outer();
//输出 function outer(){
//    inner();
//}

上面的代码输出了 outer 函数的源码。因为 outer()调用了 inner(),所以 inner.caller()就指向 outer()。当然也可以实现松耦合,可以这么做:

function outer(){
    inner();
}

function inner(){
    alert(arguments.callee.caller);
}

outer();

注意:

当函数在严格模式下运行时,访问 arguments.callee 会导致错误。ES5 还定义了 arguments.caller 属性,但在严格模式下访问它也会报错,而在非严格模式下这个属性也始终是 undefined。定义 arguments.callee 属性是为了分清 arguments.callee 和函数 caller 属性。

以上变化都是为了加强 js 这门语言的安全性,这样第三方代码就不会通过 caller 去窥探其他代码了。

严格模式还有一个限制:不能为 caller 属性赋值,否则会导致错误。

5.5 函数属性和方法

属性

前面提到过,ES 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length、prototype。其中 length 表示函数希望接收的命名参数的个数,如下:

function sayName(name){
    alert(name);
}      

function sum(num1, num2){
    return num1 + num2;
}

function sayHi(){
    alert("hi");
}

alert(sayName.length);  //1
alert(sum.length);      //2
alert(sayHi.length);    //0

另一个是 prototype 属性。对于 ES 中的引用类型而言,prototype 是保存他们所有实例方法的真正所在。换句话说,诸如 toString()、valueOf()等方法都保存在 prototype 名下,只不过是通过各自对象的实例访问罢了。后续面向对象会详情讲解 prototype。

call & apply

每个函数都包含两个非继承方法:apply()、call()。这两个的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。

首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。第二个参数可以是 Array 的实例,也可以是 arguments 对象:

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]);
}

alert(callSum1(10,10));   //20
alert(callSum2(10,10));   //20

在严格模式下,未指定环境对象而调用函数,则 this 值不会转型为 window。

除非明确把函数添加到某个对象或者调用 apply()或 call(),否则 this 值将是 undefined。

call()方法和 apply()方法作用相同,他们的区别仅在于接收参数的方式不同。对于call()方法,第一个参数是 this 值没有变化,变化的是其余参数都直接传递给函数。也就是说,在使用 call()时,传递给函数的参数必须逐个列出来:

function sum(num1, num2){
    return num1 + num2;
}

function callSum(num1, num2){
    return sum.call(this, num1, num2);
}

alert(callSum(10,10));   //20

事实上,传递参数并非 apply()和 call()真正的用武之地;他们强大的地方是能够扩充函数赖以运行的作用域。看一个例子:

window.color = "red";
var o = { color: "blue" };

function sayColor(){
    alert(this.color);
}

sayColor();            //red

sayColor.call(this);   //red
sayColor.call(window); //red
sayColor.call(o);      //blue

使用 call()和 apply()来扩充作用域的最大好处,就是对象不再需要与方法有任何耦合关系。

ES5 中还定义了 bind()方法,这个方法会创建一个函数的实例,其 this 值会被绑定到传给 bind()函数的值:

window.color = "red";
var o = { color: "blue" };
                    
function sayColor(){
    alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor();   //blue

6. 基本包装类型

为了方便操作基本类型值,ES 还提供了 3 个特殊的引用类型:Boolean、Number 和 String。这些类型与其他引用类型类似,但同时也具有与各自的基本类型相应的特殊行为。

实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。

var s1 = “some text”;
var s2 = s1.substring(2);

我们知道这个 s1 当然是基本类型值。而为什么不是对象的 s1 能调用 substring() 方法,从逻辑上不应该。其实,为了让我们呢能直观的操作,后台已经自动完成了一系列的处理。

1)创建 String 类型的一个实例;

2)在实例上调用指定的方法;

3)销毁这个实例;

var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;

上面三个步骤同样适用于 Boolean、Number

var s1 = new String("some text");
s1.color = "red";
//实际到执行这一行的时候,s1 对象已经被销毁了,这时候又创建了自己的 String 对象,而该对象没有 color 属性
alert(s1.color);

也可以显示的调用 String、Boolean、Number 来创建基本包装类型的对象。不过,应该在绝对必要的情况下。因为这种做法很容易让人分不清自己是在处理基本类型还是引用类型的值。typeof 判断对于引用类型会返回 “object”。

6.1 Boolean 类型

同理,建议不要使用 Boolean 对象。

6.2 Number 类型

toString()方法对于 Number 传入不同参数代表不同进制;

var numberObject = new Number(10);
var numberValue = 99;

//toString() using a radix
alert(numberObject.toString());       //"10"
alert(numberObject.toString(2));      //"1010"
alert(numberObject.toString(8));      //"12"
alert(numberObject.toString(10));     //"10"
alert(numberObject.toString(16));     //"a"

//toFixed()
alert(numberObject.toFixed(2));    //outputs "10.00"

numberObject = new Number(99);
alert(numberObject.toPrecision(1));    //"1e+2"
alert(numberObject.toPrecision(2));    //"99"
alert(numberObject.toPrecision(3));    //"99.0"
    
alert(typeof numberObject);   //object
alert(typeof numberValue);    //number
alert(numberObject instanceof Number);  //true
alert(numberValue instanceof Number);   //false

6.3 String 类型

6.3.1 字符方法

两个用于访问字符串中特定字符的方法是:charAt()和 charCodeAt()。

这两个方法都接收一个参数,即基于 0 的字符位置。其中 charAt() 方法以单字符串的形式返回给特定位置的那个字符:

var str = "hello world";
alert(str.charAt(1));//"e"

而 charCodeAt()则返回字符编码:

var str = "hello world";
alert(str.charCodeAt(1));//"101"
6.3.2 字符串操作方法

concat()用于将一或多个字符串拼接起来,其实用加号操作符 + 就好了。

ES 提供了三个基于子字符串创建新字符串的方法:slice()、substr()、subString()。

这三个方法都会返回被操作字符串的子字符串,而且都接收一或两个参数。

第一个参数指定子字符串的开始位置,第二个参数(在指定情况下)表示子字符串到哪里结束。

具体来说,slice()和 subString()的第二个参数指定的是子字符串最后一个字符后面的位置。而 substr()的第二个参数指定的则是返回的字符个数。如果没有传递第二个参数,则默认到结尾。与 concat()方法一样,这三个方法都不会改变字符串本身的值。

var stringValue = "hello world";
alert(stringValue.slice(3));        //"lo world"
alert(stringValue.substring(3));    //"lo world"
alert(stringValue.substr(3));       //"lo world"
alert(stringValue.slice(3, 7));     //"lo w"
alert(stringValue.substring(3,7));  //"lo w"
alert(stringValue.substr(3, 7));    //"lo worl"

如果传递负值的话行为就不一样了。

其中,slice()方法会将传入的负值与字符串长度相加,substr()方法将负的第一个参数加上字符串的长度,第二个负的值转换为 0.而 subString()方法会把所有负的都转换为 0。

alert(stringValue.slice(-3));         //"rld"
alert(stringValue.substring(-3));     //"hello world"
alert(stringValue.substr(-3));        //"rld"
alert(stringValue.slice(3, -4));      //"lo w"
alert(stringValue.substring(3, -4));  //"hel"
alert(stringValue.substr(3, -4));     //"" (empty string)
6.3.3 indexOf、lastIndexOf、trim、大小写

indexOf()、lastIndexOf()方法我们之前也讲过了,如果某个字符只出现一次,那么调用这两个方法会返回相同的位置值。

trim()方法用于删除前置及后缀的所有空格。

toLowerCase()、toUpperCase()

7. 单体内置对象

对于内置对象的定义是:由 ES 实现提供的、不依赖宿主环境的对象,这些对象在 ES 程序执行之前就已经存在了。

也就是说,开发人员不必显式的实例化内置对象,因为他们已经实例化了。之前我们所介绍的 Object、Array 和 String 这些都是内置对象。ES 中还定义了两个单体内置对象:Global 和 Math。

7.1 Global 对象

Global(全局)对象比较特别,某种意义上是作为一个终极的兜底对象来定义。换句话说,不属于任何其他对象的属性和方法,都是它的属性和方法。isNaN()、isFinite()、parseInt()这些其实都是 Global 对象的方法。

URI 编码方法

decodeURI、decodeURIComponent、encodeURI、encodeURIComponent

eval

eval()方法很强大,就像一个 ES 解析器,接收一个参数,参数是要执行的 ES 字符串。其实就是可以实现热部署,但是其实很危险,所以的代码注入,防止用户有恶意输入。

window 对象

ES 虽然没有指出如何直接访问 Global 对象,但是 Web 浏览器都是将这个全局对象作为 window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了 window 对象的属性。

var color = "red";
        
function sayColor(){
    alert(window.color);
}

window.sayColor();  //"red"

7.2 Math 对象

Math 对象提供很多实用方法。

min、max

取最大值、最小值

random

返回大于等于0 小于1 的一个随机数

舍入
  • ceil 向上取最近的整数
  • floor 乡下取最近的整数
  • round 四舍五入
alert(Math.ceil(25.9));     //26
alert(Math.ceil(25.5));     //26
alert(Math.ceil(25.1));     //26

alert(Math.round(25.9));    //26
alert(Math.round(25.5));    //26
alert(Math.round(25.1));    //25

alert(Math.floor(25.9));    //25
alert(Math.floor(25.5));    //25
alert(Math.floor(25.1));    //25

这一章有很多细碎的知识点,而且大部分都是我们常用的,可以说非常重要了,同时也是为了下面要讲的做铺垫。

因为下面要讲的可能你平时用到的不多,但是却对我们编写 JavaScript 非常关键,因为只有真正搞懂这些,了解了原理,才能手到擒来,那么这些是什么呢?

面向对象、原型链、递归、闭包、作用域等等

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant