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

设计模式 - 下 #14

Open
yizihan opened this issue Mar 10, 2018 · 0 comments
Open

设计模式 - 下 #14

yizihan opened this issue Mar 10, 2018 · 0 comments

Comments

@yizihan
Copy link
Owner

yizihan commented Mar 10, 2018

代理模式 - Proxy Pattern

当一个昂贵的对象应被实例化时,Proxy模式可以帮助我们对其进行控制,提供高级的方法来引用对象,或修改对象,让它在特定的上下文中以一种特殊方式发挥作用。

jQuery.proxy() 接受函数作为参数,并返回一个始终具有特定上下文的新对象。这确保了函数中this的值是我们所需要的值。

实例需求:按钮点击之后延迟添加类名

// 失败范例
$('#btn').on('click', function() {
    console.log(this);		// <button class="btn" id="btn">按钮</button>
    setTimeout(functioin() {
        console.log(this);	// Window  延迟执行函数中的this都指向Window
        $(this).addClass('btn-success');    // 失败,因为this指向了Window
    }, 1000);
});

// 成功范例
$('#btn').on('click', function(){
    setTimeout($.proxy(function() {
        $(this).addClass('btn-success');
    }, this), 1000);    // 调用环境中的this指向本身,利用proxy特性,将此this作为$.proxy内执行上下文
});

ES6 - Proxy

Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,属于一种“元编程”,即对编程语言进行编程。

Proxy是在目标对象架设一层“拦截”,外界对该对象的访问,都必须先通过这层“拦截”,而这层“拦截”可以对外界的访问进行过滤和改写。

var proxy = new Proxy(target, handler);
// new Proxy() 生成一个Proxy实例
// target 表示所要拦截的目标对象
// handler 一个对象,表示用来定制拦截行为
var person = {
    name: 'yizihan'
}
var proxy = new Proxy(person, {
    // 重写 (.)方法
    get: function (target, property) {
        if(property in target) {
            return target[property];
        } else {
            return new ReferenceError("Property '"+ property + "' does not exist.")
        }
    }
})
var obj = Object.create(proxy);
obj.name;   // yizihan
obj.age;    // ReferenceError: Property 'age' does not exist.

通过new Proxy()代理目标对象,目标对象内部的this会指向Proxy代理。


混入 - Mixin Pattern

在一个对象之中混入另外一个对象的方法。

首先设定Mixin包含的方法属性,然后利用原型链继承,从Mixin中继承到子类。

传统继承方式

function Person (firstname, lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
}
function SuperHero (firstname, lastname, skill) {
    // 继承Person自身属性
    Person.call(this, firstname, lastname);
    // => this.firstname = firstname;
    // => this.lastname = lastname;
    
    this.skill = skill;
}

// 继承Person原型上的属性
SuperHero.prototype = Object.create(Person.prototype);
SuperHero.prototype.constructor = SuperHero;

var superman = new SuperHero('Clark', 'Kent', ['flight'])
console.log(superman)

Mixin方式

var Car = function(setting) {
  this.model = setting.model || 'no model provided';
  this.color = setting.color || 'no color provided';
};

var Mixin = function() {};
// 测试发现将所有方法都放在一个对象中是不可行的,必须单独列出来 ???
// Mixin.prorotype = {
//   driveForward: function () {
//     console.log('drive forward');
//   },
//   driveBackward: function () {
//     console.log('drive backward');
//   }
// };
Mixin.prototype.driveForward = function () {
	console.log('drive forward');
}
Mixin.prototype.driveBackward = function () {
	console.log('drive backward');
}

// 设定继承方法函数
function inherit(receivingClass, givingClass) {
  // 如果有第三个参数,即指定需要继承的方法
  if (arguments[2]) {
    for (var i = 2; i < arguments.length; i++) {
      receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
    }
  } else {
    for (var methodName in givingClass.prototype) {
      // 判断本身是否存在该方法 
      if (!receivingClass.prototype[methodName]) {
        receivingClass.prototype[methodName] = givingClass.prototype[methodName];
      }
    }
  }
}
// 实现原型链上的方法继承
inherit(Car, Mixin, 'driveForward');
var myCar = new Car({
  model: 'Civic',
  color: 'black'
});
console.log(myCar.__proto__); 	// {driveForward: ƒ, constructor: ƒ}
myCar.driveForward();						// drive forward

Mixin 有助于减少系统中的重复功能代码及增加函数的复用。

ES6方式

export function mixins(...list) {
    return function(target) {
        Object.assign(target.prototype, ...list);
    }
}

import { mixins } from './mixins';
const Foo = {
    foo() { console.log('foo'); }
}
// 使用ES6装饰器
@mixins(Foo)
class MyClass {}

let obj = new Myclass();
obj.foo();      // foo

装饰者模式 - Decorator Pattern

Decorator 提供了将行为动态添加至系统的现有类的能力

var MacbookPro = function() {};
MacbookPro.prototype.getPrice = function () {
	return 900.00;
}
// 实例化
var myMacbookPro = new MacbookPro();

// 装饰者构造函数 接受一个实例对象
var MacbookProDecorator = function (macbook) {
	this.macbook = macbook;	
}
// 重写此实例对象的getPrice(),但不会破环原来的方法
MacbookProDecorator.prototype.getPrice = function() {
	return this.macbook.getPrice() + 100.00;
}

// 装饰实例对象
myMacbookPro = new MacbookProDecorator(myMacbookPro);
var price = myMacbookPro.getPrice();
console.log(price);			// 1000

优点:对象可以被新行为包装,然后可以继续被使用,而不必担心被修改的基本对象。这种模式使我们不必依靠大量的子类来获得同样的实现。

ES6 - Decorator

修饰器是一个对类进行处理的函数,修饰器函数的第一个参数,就是所要修饰的目标类。

// 定义修饰器
function testable(target) {
    target.isTestable = true;
}
// 调用修饰器
@testabel
class MyTestableClass { ... }
// 修饰器函数生效
MyTestableClass.isTestable;         // true

带参数的修饰器

// 传递参数
function testable(isTestable) {
    // 处理目标类
    return function(target) {
        target.isTestable = isTestable;
    }
}

@testable(true)
class MyTestableClass() {}
MyTestableClass.isTestable;     // true

@testable(false)
class MyClass() {};
MyClass.isTestable;             // false

修饰器对类的行为的改变,是在代码编译时发生的,而不是代码运行时。修饰器本质就是编译时执行的函数。

方法的修饰

修饰器不仅可以修饰类,还可以修饰类的属性。

class Person {
    // 修饰器作用于方法
    @readonly
    name() { return `${this.first} ${this.last}` }
}
// 定义修饰器
function readonly(target, name, descriptor) {
    descriptor.writable = false;
    return descriptor;
}
// @readonly 发生的调用关系等同于下面
readonly(Person.prototype, 'name', descriptor)
Object.defineProperty(Person.prototype, 'name', descriptor);
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