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

Javascript零碎之基础三 #50

Open
kekobin opened this issue Oct 10, 2019 · 0 comments
Open

Javascript零碎之基础三 #50

kekobin opened this issue Oct 10, 2019 · 0 comments

Comments

@kekobin
Copy link
Owner

kekobin commented Oct 10, 2019

面向对象(OOP)

javascript中实现OOP是通过function实现的(当然ES6的class也可以)。

function Oop(foo, bar) {
  if (!(this instanceof Oop)) {
    return new Oop(foo, bar);
  }

  this._foo = foo;
  this._bar = bar;
}

并且这个对象必须通过 new 关键字进行实例化才能使用:

var o = new Oop(111, 222);
o._foo; // 111

此时Oop里面的this代表的就是它的实例,如果没有使用new,那么就跟函数没啥区别,它里面的this指向的就是它的作用域(这里即是window)。

new 命令的原理

使用new命令时,它后面的函数依次执行下面的步骤。

1.创建一个空对象,作为将要返回的对象实例。
2.将这个空对象的原型,指向构造函数的prototype属性。
3.将这个空对象赋值给函数内部的this关键字。
4.开始执行构造函数内部的代码。

也就是说,构造函数内部,this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(即this对象),将其“构造”为需要的样子。

如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象。

new命令简化的内部流程,可以用下面的代码表示。

function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
  // 将 arguments 对象转为数组
  var args = [].slice.call(arguments);
  // 取出构造函数
  var constructor = args.shift();
  // 创建一个空对象,继承构造函数的 prototype 属性
  var context = Object.create(constructor.prototype);
  // 执行构造函数
  var result = constructor.apply(context, args);
  // 如果返回结果是对象,就直接返回,否则返回 context 对象
  return (typeof result === 'object' && result != null) ? result : context;
}

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

// 实例
var actor = _new(Person, '张三', 28);

this

var A = {
  name: '张三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};

var name = '李四';
var f = A.describe;
f() // "姓名:李四"

记住一句: 哪个对象调用的,this就指向谁。

注意: this只会指向当前一层的对象,而不会继承更上面的层。

var a = {
  p: 'Hello',
  b: {
    m: function() {
      console.log(this.p);
    }
  }
};

a.b.m() // undefined

上面示例中,this指向的是b对象,而b对象上未定义p。

绑定 this 的方法

this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。

Function.prototype.call()

函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。

image

call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象。

Function.prototype.apply()

apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。

func.apply(thisValue, [arg1, arg2, ...])

原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加。

应用
(1)找出数组最大元素

var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 15

(2)转换类似数组的对象

Array.prototype.slice.apply({0: 1, length: 1}) // [1]

Function.prototype.bind()

bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。

bind方法的参数就是所要绑定this的对象:

var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};

var func = counter.inc.bind(counter);
func();
counter.count // 1

上面代码中,counter.inc方法被赋值给变量func。这时必须用bind方法将inc内部的this,绑定到counter,否则就会出错。

bind方法有一些使用注意点:

(1)每一次返回一个新函数

bind方法每运行一次,就返回一个新函数,这会产生一些问题:

// 错误
element.addEventListener('click', o.m.bind(o));         

// 正确
var listener = o.m.bind(o);
element.addEventListener('click', listener);
//  ...
element.removeEventListener('click', listener);

(2)结合回调函数使用

var counter = {
  count: 0,
  inc: function () {
    'use strict';
    this.count++;
  }
};

function callIt(callback) {
  callback();
}

callIt(counter.inc.bind(counter));
counter.count // 1
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

1 participant