Go to comments

JavaScript call和apply

一、js精度不准

这个   0.14 * 100   就是 bug,js 有时候会发生精度一小点的偏差

console.log(0.14 * 100); // 14.000000000000002


js 尽量避免小数操作,即使要小数操作要加 Math.ceil() 向上取整,向上取整就是 0.1 都会认为是 1, Math.floor() 是向下取整

Math.ceil(123.456); // 124

Math.floor(123.9999); // 123


Math.random()  产生 0 ~ 1 之间的随机数

包含 0,不包含 1

用数学上的表示方式是  [ 0, 1 )  左闭右开,开区间的意思是不可能到 1

Math.random(); // 0.26011405302191126

Math.random(); // 0.011724293403471586

Math.random(); // 0.9351609567822634


现在产生十次,0 到 1 之间的随机小数,

然后保留两位小数变成 0.00 ~ 0.99, toFixed() 方法可把 Number 四舍五入为指定小数位数的数字

再乘以 100,生成 0 到 100 之间的整数,会发现有时候不准

for(var i = 0; i < 10; i ++){
  var num = Math.random().toFixed(2) * 100;
  console.log(num);
}

image.png


为什么不准?

跟  0.14 x 100   一样,会发生精度的小偏差


所以在处理这样的情况,一般不用 toFixed() ,直接先乘 100 然后取整,取整就没有精度问题了

for(var i = 0; i < 10; i ++){
  var num = Math.floor(Math.random() * 100);
  console.log(num);
}

网上说 toFixed() 保留两位小数有问题,其实就是 js 精度不准


js 小数点后最多能容纳多少位?

可正常计算的范围是,小数点后 15 位,小数点前 16 位

console.log(0.000000000000001 + 0.000000000000001);         //小数点后15位返回 2e-15
console.log(0.0000000000000001 + 0.0000000000000001);       //小数点后16位返回 2e-16
console.log(0.00000000000000001 + 0.00000000000000001);     //小数点后17位返回 2e-17
console.log(0.000000000000000001 + 0.000000000000000001);   //小数点后18位返回 2e-18
console.log(0.0000000000000000001 + 0.0000000000000000001); //小数点后19位返回 2e-19


小数点前 17 位就出现不精准的问题了

console.log(100000000000001 + 100000000000001);     // 小数点前15位 200000000000002
console.log(1000000000000001 + 1000000000000001);   // 小数点前16位 2000000000000002
console.log(10000000000000001 + 10000000000000001); // 小数点前17位 20000000000000000


小数点后 6 位是正常显示,

小数点后 7 位是科学计数法表示了

console.log(0.0000001 + 0.0000001); // 小数点后7位 2e-7

console.log(0.000001 + 0.000001); // 小数点后6位 0.000002


二、call和apply

作用:改变this指向

区别:后面传的参数形式不同


call 和 apply 这个知识点非常小但应用非常的广,作用是改变 this 的指向

任何一个方法都可以用 call() 方法,其实 call() 才是这个方法执行的真实面目


正常是这样  test()  执行函数

function test(){
  console.log('lalaala');
}

test();


其实 test() 执行内部会经历一个变化,这是  test.call()  它应有的面目

function test(){
  console.log('lalaala');
}

test.call();


 test() ---> test.call()  

test.call() 和正常执行 test() 是一样的


call() 有一个高深的用法,括号里面可以传参数

Person()和Person.call()是一样的,但是call()里面可以传参数

function Person(name, age){
  this.name = name;
  this.age = age;
}

var obj = {
}

Person.call(obj); // 1.把对象obj传进去会让Person里面预设的this全部变成obj

Person.call(obj, 'Glee', 37); // 2.从第二位开始传参数

console.log(obj); // 3.现在对象obj变成这样 {name: "Glee", age: 37}

不用 new 操作 Person 函数时,函数里面的 this 默认指向 window,

但是把 obj 传进去,Person.call(obj) 里面的 this 就变成了 obj

function Person(name, age){

  // 里面变化 this == obj

  obj.name = name;

  obj.age = age;

}


call() 的根本作用是改变 this 指向,

第一位参数是改变 this 的指向,

第二位参数以后的参数,当做正常的实参传到实参里面,

借用 Person 方法来实现 obj的 功能,构造完后 obj 变成  {name: "Glee", age: 37} 


比如在一个部门开发,你写的方法如果能实现我的功能,那我就不用再写了

function Person(name, age, sex){
  this.name = name;
  this.age = age;
  this.sex = sex;
}

function Student(name, age, sex, tel, grade){
  // 1.Person.call(this) 这个this就是new对象的第一步,隐式生成的this
  // var this = {name:"", age:"", sex:""}

  // 2.相当于把Person里面的三行属性拿过来了(拿到隐式的this里)
  // this.name = name;
  // this.age = age;
  // this.sex = sex;

  Person.call(this, name, age, sex);
  this.tel = tel;
  this.grade = grade;
}

var Li = new Student('Glee', 37, 'femate', 132, 2006);
console.log(Li); // Student {name: "Glee", age: 37, sex: "femate", tel: 132, grade: 2006}

Student 的功能完美涵盖了 Person 的功能,利用 Person 方法实现了 Student 功能,

这个例子是真实,实战开发的一个企业级的应用但是比较简单,call() 只有一个应用就是改变 this 指向,改变 this 指向导出了一个功能,叫借用别人的函数实现自己的功能。


工厂车间模块化造车,Wheel 轮子、sit 座椅、Model 汽车框架等,把这些东西组装起来

function Wheel(wheelSize, style){
  this.weelSize = wheelSize; // 车轮尺寸
  this.style = style;        // 车轮风格
}

function Sit(comfortable, sitColor){
  this.comfortable = comfortable; // 座椅舒适程度
  this.sitColor = sitColor;       // 座椅颜色
}

function Model(height, width, len){
  this.height = height;  // 汽车模型的长
  this.width = width;    // 汽车模型的宽
  this.len = len;        // 汽车模型的高
}

function Car(wheelSize, style, comfortable, sitColor, height, width, len){
  Wheel.call(this, wheelSize, style);
  Sit.call(this, comfortable, sitColor);
  Model.call(this, height, width, len);
}

var car = new Car('车轮直径100CM', '黑色的车轮', '真皮座椅', '座椅卡其色', '车高1800CM', '车长1900CM', '车重4900公斤');
console.log(car); // Car {weelSize: "车轮直径100CM", style: "黑色的车轮", comfortable: "真皮座椅", sitColor: "座椅卡其色", height: "车高1800CM", …}

造车的工厂 Car 函数里一行都没写,用的都是别的工厂的零部件,这是企业里协调配合开发,总有一些人在写零部件,零部件就是基础框架(编程都用的东西,会先写好),

当然企业级的开发不可能这么简单,this就等于一行(this.height = height),后面可能等于一个立即执行函数一顿处理,完后把结果返回。


apply() 和 call() 功能是一样的几乎没区别,唯一一点区别传参列表不一样,

第一位都是改变 this 指向的对象,

第二位 call() 一位一位的传实参进去,apply() 只能传一位实参并且传的实参必须是数组形式,也就是说 apply() 必须传一个数组。


call() 需要把实参按照形参的个数传进去,apply() 需要传一个arguments

function Wheel(wheelSize, style){
  this.weelSize = wheelSize;
  this.style = style;
}

function Sit(comfortable, sitColor){
  this.comfortable = comfortable;
  this.sitColor = sitColor;
}

function Model(height, width, len){
  this.height = height;
  this.width = width;
  this.len = len;
}

function Car(wheelSize, style, comfortable, sitColor, height, width, len){
  Wheel.apply(this, [wheelSize, style]);
  Sit.apply(this, [comfortable, sitColor]);
  Model.apply(this, [height, width, len]);
}

var car = new Car('车轮直径100CM', '黑色的车轮', '真皮座椅', '座椅卡其色', '车高1800CM', '车长1900CM', '车重4900公斤');


58同城的面试题

JavaScript的call和apply方法是做什么的,两者有什么区别?改变this指向,传参列表不同。



Leave a comment 0 Comments.

Leave a Reply

换一张