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

【js基础】 #11

Open
jc-wow opened this issue Oct 12, 2020 · 2 comments
Open

【js基础】 #11

jc-wow opened this issue Oct 12, 2020 · 2 comments

Comments

@jc-wow
Copy link
Owner

jc-wow commented Oct 12, 2020

1.继承

JavaScript深入之从原型到原型链
JavaScript常用八种继承方案

组合寄生继承实现:

function Class(classNum) {
  this.class = classNum;
}

Class.prototype.getPosition = function(position) {
  console.log(position);
}

function Student(classNum, name, age) {
  Class.call(this, classNum);
  this.name = name;
  this.age = age;
}

// Student.prototype = new Class(); // Student的实例和Student的原型对象都会拷贝一份构造函数的属性,浪费内存 
Student.prototype = Object.create(Class.prototype);  // 目的是访问到Class的原型对象, 代替上一步
Student.prototype.constructor = Student;
const Mike = new Student(5, 'Mike', 10);
console.log(Mike.class, Mike.name, Mike.age);  // 5, 'Mike', 10
Mike.getPosition('Floor 3') // 'Floor 3'

2.原型链

JavaScript深入之从原型到原型链
继承与原型链

每个实例对象( object )都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

3.call,apply,bind

JavaScript深入之call和apply的模拟实现
JavaScript深入之bind的模拟实现
call实现:

Function.prototype.mycall = function(ctx) {
  let ctx = ctx || window;  //如果ctx为null则ctx指向window
  ctx.fn = this;  
  const args = [];
  for (let i = 1, l = arguments.length; i < l; i++) {
    args.push(arguments[i]);
 }
  res = ctx.fn(...args);
  delete ctx.fn;
  return res;
}

apply实现:

Function.prototype.myreply = function(ctx, arr) {
  let ctx = ctx || window;  //如果ctx为null则ctx指向window
  ctx.fn = this;
  let res = null;
  if (!arr) {
    res = ctx.fn();
  } else {
    const args = [];
    for (let i = 0, l = arr.length; i < l; i++) {
      args.push(arr[i]);
    }
    res = ctx.fn(...args);
 }
  delete ctx.fn;
  return res;
}

bind实现:

// 支持传参
// 支持柯里化
// 支持new运算符调用,只是被绑定的this被忽略,
// 且返回的实例还是会继承构造函数的构造器属性与原型属性,并且能正常接收参数
Function.prototype.mybind = function(ctx) {
  if (typeof this!== 'function') {
    throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
  }
  const self = this;
  const argsArr = Array.from(arguments);
  const args = argsArr.slice(1, argsArr.length);
  const fbound = function() {
    const argus = Array.from(arguments);
    // 如果new运算符创建实例,则this指向构造函数的prototype,否则指向ctx。
    return self.apply(this instanceof fbound ? this : ctx, args.concat(argus));
  }
  fbound.prototype = Object.create(this.prototype);
  return fbound;
}

4.new

/**
 * 模拟实现 new 操作符
 * @param  {Function} ctor [构造函数]
 * @return {Object|Function|Regex|Date|Error}      [返回结果]
 */
function newOperator(ctor){
    if(typeof ctor !== 'function'){
      throw 'newOperator function the first param must be a function';
    }
    newOperator.target = ctor;
    var newObj = Object.create(ctor.prototype);
    var argsArr = Array.from(arguments);
    var ctorReturnResult = ctor.apply(newObj, argsArr.slice(1));
    var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null;
    var isFunction = typeof ctorReturnResult === 'function';
    if(isObject || isFunction){
        return ctorReturnResult;
    }
    return newObj;

5.防抖和节流

JavaScript专题之跟着 underscore 学节流
7分钟理解JS的节流、防抖及使用场景
节流:

<html>
  <head></head>
  <body>
    <div style="height: 100%; width: 100%"></div>
  </body>
  <script>
    let a = 1;
    const selector = document.querySelector("div");
    function callback() {
      selector.textContent = a;
      a++;
    }
    // 延迟执行,行为停止后再执行一次
    const delayThrottle = (fn, time) => {
      let timer = null;
      return function () {
        if (!timer) {
          timer = setTimeout(() => {
            timer = null;
            fn();
          }, time);
        }
      };
    };
    // 立即执行,行为停止后立即停止调用
    const immeThrottle = (fn, time) => {
      let preTime = 0;
      return function () {
        const curTime = +new Date();
        if (curTime - preTime > time) {
          fn();
          preTime = curTime;
        }
      };
    };
    // 立即执行并延迟调用
    const mixThrottle = (fn, time) => {
      let preTime = 0,
        timer = null;
      const later = () => {
        preTime = +new Date();
        timer = null;
        fn();
      };
      const throttle = () => {
        const curTime = +new Date();
        const remaining = time - (curTime - preTime);
        // 如果没有剩余时间或者修改系统时间
        if (remaining <= 0 || remaining > time) {
          if (timer) {
            clearTimeout(timer);
            timer = null;
          }
          preTime = curTime;
          fn();
        } else if (!timer) {
          timer = setTimeout(later, remaining);
        }
      };
      return throttle;
    };
    // option参数判断是否立即执行或者延迟结束
    // leading: false 禁止立即执行
    // trailing: false 禁止延迟回调
    function optimizeThrottle(fn, time, options) {
      let context,
        args,
        preTime = 0,
        timer = null;
      if (!options) options = {};
      const later = function () {
        preTime = !options.leading ? 0 : +new Date();
        timer = null;
        fn.apply(context, args);
        if (!timer) context = args = null;
      };
      const throttle = function () {
        const curTime = +new Date();
        if (!preTime && !options.leading) preTime = curTime;
        const remaining = time - (curTime - preTime);
        context = this; // 修正this指向
        // 如果没有剩余时间或者修改系统时间
        if (remaining <= 0 || remaining > time) {
          if (timer) {
            clearTimeout(timer);
            timer = null;
          }
          preTime = curTime;
          fn.apply(context, args);
          if (!timer) context = args = null;
        } else if (!timer && !options.trailing) {
          timer = setTimeout(later, remaining);
        }
      };
      return throttle;
    }
    //document.addEventListener('mousemove', delayThrottle(callback, 1000));
    //document.addEventListener("mousemove", immeThrottle(callback, 1000));
    document
      .querySelector("div")
      .addEventListener("mousemove", optimizeThrottle(callback, 3000));
  </script>
</html>

防抖:

<html>
  <head></head>
  <body>
    <div style="height: 100%; width: 100%"></div>
  </body>
  <script>
    let a = 1;
    const selector = document.querySelector("div");
    function callback(e) {
      console.log(e);
      selector.textContent = a;
      a++;
    }
    function debounce(fn, time) {
      let timer = null;
      return function () {
        let context = this,
          args = arguments;
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(context, args), time);
      };
    }
    document
      .querySelector("div")
      .addEventListener("mousemove", debounce(callback, 1000));
  </script>
</html>

// 应用场景
debounce:
search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
throttle:
鼠标不断点击触发,mousedown(单位时间内只触发一次)
监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

6.event loop

6.1浏览器中

  • js执行的时候,会把不同的变量存放与stack和heap中,heap中存放对象,stack中存放基础类型变量和栈指针
  • 当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。 而当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。
  • js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码...,如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”的原因。
    event loop

6.2node中

node

  • 定时器:本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数
  • 待定回调:执行延迟到下一个循环迭代的 I/O 回调(此阶段对某些系统操作(如 TCP 错误类型)执行回调)
  • 轮询:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞
  • 检测:setImmediate() 回调函数在这里执行
  • 关闭的回调函数:一些关闭的回调函数,如:socket.on('close', ...)

7.map/weakMap

  • Map 对象的键可以是任何类型,但 WeakMap 对象中的键只能是对象引用;
  • WeakMap 不能包含无引用的对象,否则会被自动清除出集合(垃圾回收机制);
  1. Map对象创建保存着引用key和引用value的两个数组,清除原始对象后引用中的key和value并不会消失(强引用);
  2. 相比之下,原生的 WeakMap 持有的是每个键对象的 “弱引用”,这意味着在没有其他引用存在时垃圾回收能正确进行;
  • WeakMap 对象是不可枚举的,无法获取集合的大小。
let obj = {
  'a': 123
}
let map = new  Map();
map.set(obj, [1,2,3,4]);
obj = null;
console.log(...map.keys()) // {'a': 123}
console.log(...map.values()) // [1,2,3,4]

8.变量提升

变量提升
在JavaScript代码运行之前其实是有一个编译阶段的。编译之后才是从上到下,一行一行解释执行。变量提升就发生在编译阶段,它把变量和函数的声明提升至作用域的顶端。(编译阶段的工作之一就是将变量与其作用域进行关联)。
变量提升需要注意两点:
1.提升的部分只是变量声明,赋值语句和可执行的代码逻辑还保持在原地不动
2.提升只是将变量声明提升到变量所在的变量范围的顶端,并不是提升到全局范围

@jc-wow jc-wow changed the title 【js基础】继承 【js基础】 Oct 14, 2020
@kimi233
Copy link

kimi233 commented Jul 18, 2021

原型链继承中
Student.constructor应该为Function
应该是这样把
Student.prototype.constructor = Student;

@jc-wow
Copy link
Owner Author

jc-wow commented Aug 29, 2021

原型链继承中
Student.constructor应该为Function
应该是这样把
Student.prototype.constructor = Student;

是的,应该挂到Student的原型对象上。

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

No branches or pull requests

2 participants