待更

执行上下文

js的每一行代码javascript引擎都会创建执行上下文( if/for等级块没有,需依附其他的作用域),每个执行上下文都有三个属性(变量对象,作用域链,this)但是也有不同:
全局:一个程序只有一个,默认的执行上下文,全局对象通常以this代之(绑定全局对象);
函数:每个函数在调用的时候都会创建执行上下文,一个程序可以有多个函数执行上下文;
Eval:Eval比较特殊,单独列出,不建议使用;
而执行上下文一般都和执行栈(调用栈)相关联,用于存储程序所创建的执行上下文,而引擎会执行位于栈顶的执行上下文,当函数执行完毕时,改函数的执行上下文被弹出执行栈而栈的数据存储方式为先进后出的规则。
考点:引出执行栈,this指向(修改this方法),事件系统等

this指向

无指定情况下,this一般指全局对象,浏览器环境为window,node为global等,this的指向是由调用时决定的,而非创建时决定。
可通过applycallbind方法实现修改this,指向某个对象,都是Function原型链中Function.Prototype的属性,具体用法如下:
apply:调用方法:Obj.apply(obj, [params]),使得Obj的执行函数里的this指向obj,传参方式是通过数组的形式,自执行;
callObj.call(obj, arguments),和apply的区别在于第二个参数的类型,是类数组,自执行;
bind:Obj.call(obj)(),创建一个新函数,执行时需手动调用,之后的参数根据原函数形参的个数决定,返回的函数可通过new调用;


以上三种方法适用于非箭头函数或非严格模式下,前者this指向全局,后者普通函数没有this,只有全局的,applycall传入的objundefined时,会被替换成全局对象。

//  手写call、apply、bind方法
Function.prototype._apply = function(ctx){
  const arg = arguments[1]; // 获取数组形式的入参
  const fn = Symbol(); // 保证属性唯一性,通过Symbol创建
  let ctx = ctx || window;
  ctx[fn] = this;
  // 执行函数
  if (arg) ctx.fn();
  else ctx.fn(...arg);
  delete ctx.fn; // 上下文中删除函数引用
}
const Obj = {
  name: "bob",
  getName: function() {
    console.log(this.name)
  }
};
const obj = { name: "alice" };
Obj.getName();  // bob
Obj.getName._apply(obj); // alice
Function.prototype._bind = function(ctx) {
  if (typeof this !== 'function') throw new  TypeError (`${this} must be a function`)
  const self = this;
  const args = [].slice.call(arguments, []);
  const bind = function() {
    const parms = [].slice.call(arguments);
    return self.apply(ctx, args.concat(parms));
  }
  return bind;
}
const obj = { name: "alice" };
function test (a, b) {
  console.log(this.name);
  console.log(a, b);
}
const bind = test._bind(obj, 1);
bind(2); 
// alice 
// 1, 2

复杂实现方式
Function.prototype.bind2 = function (context) {
   if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};
    var fbound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        // 当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面一句 `fbound.prototype = this.prototype;`,已经修改了 fbound.prototype 为 绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。
        // 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 context。
        self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    }
    // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承函数的原型中的值,但是直接修改 fbound.prototype 的时候,也会直接修改函数的 prototype,所以可以通过空函数作为一个中转
   //  fbound.prototype = this.prototype;
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
    return fbound;
}

模拟new的方式实现

function objectFactory() {
  var obj = object.create(null), Constructor = [].shift.call(arguments); 
  obj.__proto__ = Constructor.prototype;
  var ret = Constructor.apply(obj,arguments);
   return typeof ret ==='object '? ret : obj;
};

function Otaku(name, age) {
  this.name = name;
  this.age = age;
  this.habit ='Games';
}

Otaku.prototype.strength =60;
Otaku.prototype.sayYourName =function() {
  console.log('I am '+this.name);
}
var person = objectFactory(Otaku,'Kevin','18')
console.log(person.name)// Kevin
console.log(person.habit)// Games
console.log(person.strength)// 60
person.sayYourName();// I am Kevin

你可能感兴趣的:(待更)