JS-day4

 

 

day4

[js] 你对new操作符的理解是什么?手动实现一个new方法

对于:

var obj=new Base();复制代码

new操作符具体干了什么?其实就三件事情。

var obj={};

obj.__proto__=Base.prototype;

Base.call(obj);复制代码
  • 第一步,创建一个空对象
  • 第二步,将空对象的__proto__属性指向Base函数对象的prototype成员对象
  • 第三步,将Base函数对象的this指针替换成obj,然后调用Base函数
  • 最后,考察第三步的返回值,如果无返回值或者返回一个非对象值,则将obj返回作为新对象;否则将返回值作为新对象返回。

要手动实现一个 new 操作符,首先要知道 new 操作符都做了什么事,即构造函数的内部原理:
1.创建一个新对象;
2.链接到原型(将构造函数的 prototype 赋值给新对象的 __proto__);
3.绑定this(构造函数中的this指向新对象并且调用构造函数)
4.返回新对象
这样我们就可以手动实现一个 new 方法了

function realizeNew () {
    //创建一个新对象
    let obj  = {};
    //获得构造函数
    let Con = [].shift.call(arguments);
    //链接到原型(给obj这个新生对象的原型指向它的构造函数的原型)
    obj.__proto__ = Con.prototype;
    //绑定this
    let result = Con.apply(obj,arguments);
    //确保new出来的是一个对象
    return typeof result === "object" ? result : obj
}
function Person (name,age){
    this.name = name;
    this.age = age;
    this.say = function () {
        console.log("I am " + this.name)
    }
}
 
//通过new创建构造实例
let person1 = new Person("Curry",18);
console.log(person1.name);      //"Curry"
console.log(person1.age);       //18
person1.say();      //"I am Curry'
 
//通过realize()方法创造实例
let person2 = realizeNew (Person,"Curry",18);
console.log(person2.name);      //"Curry"
console.log(person2.age);       //18
person2.say();      //"I am Curry'

[js] 0.1 + 0.2、0.1 + 0.3和0.1 * 0.2分别等于多少?并解释下为什么?

计算机内部存储数据的编码的时候,0.1在计算机内部根本就不是精确的0.1,而是一个有舍入误差的0.1。当代码被编译或解释后,0.1已经被四舍五入成一个与之很接近的计算机内部数字,以至于计算还没开始,一个很小的舍入错误就已经产生了。这也就是 0.1 + 0.2 不等于0.3 的原因。

对于 0.1 + 0.2 这样的运算,操作数会先被转成二进制,然后再计算:
0.1 => 0.0001 1001 1001 1001…(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)

  双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100110011001100110011001100110011001100... 因浮点数小数位的限制而截断的二进制数字,这时候,再把它转换为十进制,就成了 0.30000000000000004 。

[js] 如何快速让一个数组乱序,写出来

//创建一个数组
let arr = Array(100000).fill(0).map((item, index) => index + 1);
console.log(arr);
 
//1. 直接利用sort进行排序,有漏洞,大部分元素位置没有移动
arr.sort((a, b) => (Math.random() > 0.5 ? -1 : 1));
console.log(arr);
 
//2. 经典洗牌算法实现
function shuffle(array) { 
    let arrayLength = array.length,   
        randomIndex, //随机数   
        tempItem; //临时存储元素  
    for (let i = arrayLength - 1; i >= 0; i--) {    
        randomIndex = Math.floor(Math.random() * (i + 1));    
        tempItem = array[randomIndex];    
        array[randomIndex] = array[i];    
        array[i] = tempItem;  
    }  
    return array;
}
console.log(shuffle(arr));复制代码

[js] 写一个判断设备来源的方法

// 判断移动端设备
function deviceType(){
    var ua = navigator.userAgent;
    var agent = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];    
    for(var i=0; i0){         
            break;
        }
    }
}
deviceType();
window.addEventListener('resize', function(){
    deviceType();
})


// 判断微信浏览器
function isWeixin(){
    var ua = navigator.userAgent.toLowerCase();
    if(ua.match(/MicroMessenger/i)=='micromessenger'){
        return true;
    }else{
        return false;
    }
}

[js] 说说bind、call、apply的区别?并手写实现一个bind的方法

call、apply和bind的作用都是改变this的指向。其中,call和apply的区别在于传参的方式不同。call是一个一个的传,apply可以将参数以数组的形式传进去。

call 接收多个参数,第一个为函数上下文也就是this,后边参数为函数本身的参数。

apply接收两个参数,第一个参数为函数上下文this,第二个参数为函数参数,通过一个数组的形式传入。如下例所示:

let a = {
    value: 1
}
function getValue(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value)
}
getValue.call(a, 'yck', '24')
getValue.apply(a, ['yck', '24'])

而bind 和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 bind 实现柯里化。

let a = {
    value: 1
}
function getValue(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value)
}
let fn = getValue.bind(a)
fn('yck', '24)

手写实现

call

思路是给新的对象添加一个函数,然后在执行完以后删除

Function.prototype.myCall = function (context) {
  var context = context || window
  // 给 context 添加一个属性
  // getValue.call(a, 'yck', '24') => a.fn = getValue
  context.fn = this
  // 将 context 后面的参数取出来
  var args = [...arguments].slice(1)
  // getValue.call(a, 'yck', '24') => a.fn('yck', '24')
  var result = context.fn(...args)
  // 删除 fn
  delete context.fn
  return result
}

apply

Function.prototype.myApply = function (context) {
  var context = context || window
  context.fn = this

  var result
  // 需要判断是否存储第二个参数
  // 如果存在,就将第二个参数展开
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }

  delete context.fn
  return result
}

bind

Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

 

[js] 说说你对arguments的理解,它是数组吗?

一、在函数调用的时候,浏览器每次都会传递进两个隐式参数
    函数的上下文对象this
    封装实参的对象arguments
二、arguments 对象
    arguments 对象实际上是所在函数的一个内置类数组对象
    每个函数都有一个arguments属性,表示函数的实参集合,这里的实参是重点,就是执行函数时实际传入的参数的集合。arguments不是数组而是一个对象,但它和数组很相似,所以通常称为类数组对象,以后看到类数组其实就表示arguments。arguments对象不能显式的创建,它只有在函数开始时才可用。
    arguments还有属性callee,length和迭代器Symbol。
    arguments同样具有length属性,arguments.length 为函数实参个数,可以用arguments[length]显示调用参数
    arguments对象可以检测参数个数,模拟函数重载
 

[js] 解释下这段代码的意思

[].forEach.call($$("*"),function(a){ a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16) })

1.call:call(thisObj,arg1,arg2,arg3)

1

[].forEach.call($$("*"),

  就是用$$('a')来替代[],

好 那么到了第二个问题$$('a')是什么意思

2.

$$('a')

你可以在自己的浏览器上面运行一下,就是页面上所有的a标签

然后再继续

3.

function(a){}

无疑就是$$('a')组成的数组要进行的回调函数了

好我们再看里面的东西

4.~~

看在浏览器上面的运行

var a=12.233
~~a
12

var b=-123.455
~~b
-123

所以~~的作用就相当于parseInt

5.1<<24

也就是1向左移24位

也就是2的24次方

6.toString(16)

就是把数字转换成16进制的字符串

你可能感兴趣的:(JS)