知识点补充 toString和toFixed
(1)toString
我们以前讲过,说 undefined 和 null 不能调用 toString,会报错的,现在我们就知道原因了:数字能调用 toString 是因为数字可以调用包装类,包装成对象然后一层一层 往上访问一直到终端 Object.prototype,所以数字是肯定有 toString 的,但是 undefined 和 null 是没有包装类的,他就是一个原始值,他也没有原型,所以他俩不可能有 toString。现在我们就来研究一下 toString
我们访问 true.toString()就得到“true”,我们访问 123.toString()就会报错,因为他会认为这个数字是浮点型的,是小数点,小数点后必须跟数字啊,跟字母肯定不行, 所以会报错,我们 var num = 123;然后访问 num.toString()得到“123”,但是我们 var obj = {};然后访问 obj.toString()得到"[object Object]",这是为啥?不是应该返回字符串的大括号吗?
其实上边的 obj.toString()他毋庸置疑拿的就是 Object.prototype 里的 toString,但是你 var num = 123,然后 num.toString(),他就会隐式的经过包装类 new Number(num).toString(),这里的原型就是 Number.prototype,Number.prototype 的原型才是 Object.prototype,但是在 Number.prototype 里就有这个 toString 方法,即 Number.prototype.toString = function(){…},这就形成了一个原型链,那么假如说 Number 上有这个 toString 方法的话我就不用你 Object 上的了,然而 Number 上正好有这个方法,所以 Number 调用的 toString 是自己重写过的,原型上有这个方法,我又写了一个同一个名字不同功能的方法叫做重写。比如说:
Person.prototype = {
toString : function(){
return "hehe";
}
}
function Person(){
}
var person = new Person();
我们假如 Person.prototype 里边不写东西的话,调用 person.toString()他就会拿到 Object.prototype 里的 toString,输出"[object Object]",但是我们在近处写一个 toString,这时候你再访问 person.toString 就得到“hehe”,因为我们在近处截断了。 这种和原型链上名字一样的方法但实现不同功能的就叫方法的重写,重写就是覆盖。
其实系统内部把 Number、Array、Boolean、String 这些里的原型 prototype 都把 toString 重写了,所以 var num = 123,访问 num.toString()就是‘123’.因为 Object.prototype 里的 toString 没有啥用,所以他的后代子孙都重写了这个方法。 注意:我们之前讲的 document.write 不能算是真正意义上的打印,他会调用 toString 方法,然后给你返回,虽然你 var num =123;document.write(num)可以打印 123, 但是比如你 var obj = {},在 document.write(obj)就是[object Object],var obj=Object.create(null),在 document.write(obj)就会报错,因为你没有原型就 没有 toString 方法,所以 document.write 不是真正的输出方法,我们一般用 console.log。
(2)toFixed
这里讲一个小bug哈,比如说我们在控制台访问0.14*100就得到14.000000000000002, 这是一个无法解决的 bug,是因为 js 精度不准,数据会发生一小点的偏差,所以要尽量避免小数操作,即使有小数操作,也要借用两个工具: 向上取整:例如 Math.ceil(123.234)得到 124,向上取整 0.1 他也会认为是 1 向下取整:例如 Math.floor(123.999)得到 123,不管后边有多少位小数,都把他们割掉。
产生随机数:Math.random(),调用一次产生一个随机数,0-1 中间的随机小数,两边不会到头。比如说:
for(var i = 0; i<10; i++){
var num = Math.floor(Math.random() * 100);
console.log(num);
}
我们不保留有效数字位了,直接乘以 100 然后取整即可。
考点:js 可正常计算的范围:小数点前 16 位,后 16 位,有科学计数法的除外,比如 0.000000000000000001 + 0.000000000000000001 就得 2e-18(2 乘以 10 的-18 次方)。 但是你 0.10000000000000001 + 0.000000000000000001 他就输出 0.1。
(3)call 和 apply
call 和 apply 都是更改 this 指向的
①:call
比如说:
function test(){
}
test();
任何一个方法都可以.call,其实方法的执行就是方法.call 执行,例如上边的,test() 就相当于 this.call(),call 还有一个更高深的用法:
function Person(name,age){
this.name = name;
this.age = age;
}
var obj = {};
Person.call(obj);
call 后边的括号里是可以传东西的,比如传一个 obj 进去,他就会引导着 Person 发生很大的变化。我们在以前讲到如果在 Person 底下 new 的话,this 就代表了构造函数所产生的对象,我们假如说没有 new 的话,this 默认是指向 window 的,但是我们在 call 里传 obj 了,那么这个 this 就不是默认的了,就变成 obj 了,就是 call 里边传谁进去,this 就是谁。等于说里边现在执行的时候就是 obj.name = name,obj.age = age, 即使是这样,依然可以传参:
Person.call(obj,'cheng',300);
现在访问obj就得到{name: “cheng”, age: 300},因为call括号里的第一位会改变this 指向,第一位以后都会当成正常的实参传到形参里边去,所以这里边第二个实参对应的是函数执行的第一位形参,第三个实参对应的是函数执行的第二位形参。所以 call 的根本原因是改变 this 指向,借用 Person 的执行封装了 obj,借用你的方法来实现我 的功能。
应用:我们在开发的时候讲究的是快准狠,首先,快,同事之间开发,你写的方法可以实现我的功能,那我就不用写了。
例:
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name,age,sex,tel,grade){
Person.call(this,name,age,sex);
this.tel = tel;
this.grade = grade;
}
var student = new Student("sunny",123,"male",139,2017);
解析:这是一个非常好的例子,这个 call 的作用只有一个,就是改变 this 指向,那 么就倒出来借用别人的函数来实现自己的功能,你 Person.call()他就一定会执行 Person,然而里边传了一个 this,我们在外边 new 了,那么这个 this 就代表 student, 我们再来看 Student 这个函数,预编译第三步的时候实参和形参就相互统一了,而预编译发生在函数执行的前一刻,等于说执行第一句 Person.call(this,name,age,sex); 的时候 Student 里的形参已经有值了,所以第一句 call 括号里拿的是 Student 传好的形参,是具体的值,然后在把他作为实参传到 Person 里,然后利用 Person 的方法实现了自己的一个封装,其实 call 写在那里就相当于把 Person 里边的三条语句拿到 Student 里了。封装后 student 里就有 name、age 和 sex 了,再执行自己的两行,加上 tel和grade。完事后我们访问student就得到Student{name: “sunny”, age: 123, sex: “male”, tel: 139, grade: 2017}。但是这里你自己的需求必须完全涵盖人家的需求, 比如说你不想用 sex 那是不行的,你用 call 了,那么这三条语句一定会走下来。
②:apply
call 和 apply 基本上是一样的,只是写法不一样,call 需要把实参一个个传进去,但是 apply 只能传一个值 arguments,即实参列表:
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name,age,sex,tel,grade){
Person.apply(this,[name,age,sex]);
this.tel = tel;
this.grade = grade;
}
var student = new Student("sunny","123","male",139,2017);
其实非常简单,arguments 是数组,那么就直接加一个[ ]就行了,但是记住 this 不能加在里边。
考点: call 和 apply 的作用和区别:他俩都是改变 this 指向,区别传参列表不同。