代理(Proxy)是一种可以拦截并改变底层JavaScript引擎操作的包装器,在新语言中通过它暴露内部运作的对象,从而让开发者可以创建内建的对象。

数组问题

在ECMAScript6出现之前,开发者不能通过自己定义的对象模仿JavaScript数组对象的行为方式。当给数组的特定元素赋值时,影响到该数组的length属性,也可以通过length属性修改数组元素。

let colors = ["red", "green", "blue"];

console.log(colors.length); // 3

colors[3] = "black";

console.log(colors.length); // 4
console.log(colors[3]); // "black" colors.length = 2; console.log(colors.length); // 2
console.log(colors[3]); // undefined
console.log(colors[2]); // undefined
console.log(colors[1]); // "green"

Note

数值属性和length属性具有这种非标准行为,因而在ECMAScript中数组被认为是奇异对象(exotic object,与普通对象相对)。

代理和反射

调用 new Proxy() 可创建代替其他目标(taget)对象的代理,它虚拟化了目标,所以二者看起来功能一致。

代理可以拦截 JavaScript 引擎内部目标的底层对象操作,这些底层操作被拦截后会触发响应特定操作的陷阱函数。

反射API可以Reflect对象的形式出现,对象中方法的默认特性与相同的底层操作一致,而代理可以覆写这些操作,每个代理陷阱对应一个命名和参数都相同的Reflect方法。

代理陷阱 覆写的特性 默认特性
get 读取一个属性值 Reflect.get()
set 写入一个属性值 Reflect.set()
has in 操作符 Reflect.has()
deleteProperty delete 操作符 Reflect.deleteProperty()
getPrototypeOf Object.getPrototypeOf() Reflect.getPrototypeOf()
setPrototypeOf Object.setPrototypeOf() Reflect.setPrototypeOf()
isExtensible Object.isExtensible() Reflect.isExtensible()
preventExtensions Object.preventExtensions() Reflect.preventExtensions()
getOwnPropertyDescriptor Object.getOwnPropertyDescriptor() Reflect.getOwnPropertyDescriptor()
defineProperty Object.defineProperty() Reflect.defineProperty()
ownKeys Object.keys()、Object.getOwnPropertyNames()和Object.getOwnPropertySymbols() Reflect.ownKeys()
apply 调用一个函数 Reflect.apply()
construct 用new调用一个函数 Reflect.construct()

创建一个简单的代理

Proxy构造函数有两个参数

  • 目标(target)
  • 处理程序(handler)

    处理程序是定义一个或多个陷阱的对象,在代理中,出了专门为操作定义的陷阱外,其余操作均使用默认特性。

    不使用任何陷阱的处理程序等价于简单的转发代理。
let target = {};
let proxy = new Proxy(target, {}); proxy.name = "proxy";
console.log(proxy.name); // "proxy"
console.log(target.name); // "proxy" target.name = "target";
console.log(proxy.name); // "target"
console.log(target.name); // "target"

使用set陷阱验证属性

set陷阱接受4个参数:

  • trapTarget

    用于接收属性(代理的目标)的对象
  • key

    要写入的属性键(字符串或Symbol类型)
  • value

    被写入属性的值
  • receiver

    操作发生的对象(通常是代理)

Reflect.set()是set陷阱对应的反射方法和默认特性,它和set陷阱一样也接受同样的4个参数。

let target = {
name: "target"
}; let proxy = new Proxy(target, {
set(trapTarget, key, value, receiver) {
if (!trapTarget.hasOwnProperty(key)) {
if (isNaN(value)) {
throw new TypeError("属性必须是数字");
}
} return Reflect.set(trapTarget, key, value, receiver);
}
}); proxy.count = 1;
console.log(proxy.count); // 1
console.log(target.count); // 1 proxy.name = "proxy";
console.log(proxy.name); // "proxy"
console.log(target.name); // "proxy" // 抛出错误:
// Uncaught TypeError: 属性必须是数字
proxy.anotherName = "proxy";

用get陷阱验证对象结构(Object Shape)

get陷阱接受3个参数:

  • trapTarget

    被读取属性的源的对象(代理的目标)
  • key

    被读取的属性键
  • value

    操作发生的对象(通常是代理)

Reflect.get()也接受同样3个参数并返回属性的默认值。

let proxy = new Proxy({}, {
get(trapTarget, key, receiver) {
if (!(key in receiver)) {
throw new TypeError("属性" + key + "不存在");
} return Reflect.get(trapTarget, key, receiver);
}
}); proxy.name = "proxy";
console.log(proxy.name); // "proxy" // 抛出错误:
// Uncaught TypeError: 属性nme不存在
console.log(proxy.nme);

使用has陷阱隐藏已有属性

可以用in操作符来检测给定对象中是否含有某个属性,如果自由属性或原型属性匹配的名称或Symbol就返回true。

let target = {
value: 42
}; console.log("value" in target); // true
console.log("toString" in target); // true

在代理中使用has陷阱可以拦截这些in操作并返回一个不同的值。

in陷阱接受2个参数:

  • trapTarget

    读取属性的对象(代理的目标)
  • key

    要检查的属性值(字符串或Symbol)
let target = {
name: "target",
vlaue: 42
}; let proxy = new Proxy(target, {
has (trapTarget, key) {
if (key === "value") {
return false;
} else {
return Reflect.has(trapTarget, key);
}
}
}); console.log("value" in proxy); // false
console.log("name" in proxy); // true
console.log("toString" in proxy); // true

用deleteProperty陷阱防止删除属性

delete操作符可以从对象中删除属性,如果成功则返回true,不成功则返回false。

在严格模式下,如果你尝试删除一个不可配置(nonconfigurable)属性则会导致程序抛出错误,而在非严格模式下只是返回false。

每当通过delete操作符删除对象属性时,deleteProperty陷阱都会被调用,它接受2个参数:

  • trapTarget

    要删除属性的对象(代理的目标)
  • key

    要删除的属性键(字符串或Symbol)

Reflect.deleteProperty()方法为deleteProperty陷阱提供默认实现,并且接受同样的两个参数。

let target = {
name: "target",
value: 42
}; let proxy = new Proxy(target, {
deleteProperty(trapTarget, key) {
if (key === "value") {
return false;
} else {
return Reflect.deleteProperty(trapTarget, key);
}
}
}); console.log("value" in proxy); // true let result1 = delete proxy.value;
console.log(result1); // false console.log("value" in proxy); // true console.log("name" in proxy); // true let result2 = delete proxy.name;
console.log(result2); // true console.log("name" in proxy); // false

原型代理陷阱

ES6中新增的Object.setPrototypeOf()方法,它被用于作为ES5中的Object.getPrototype()方法的补充。通过代理中的setPrototypeOf陷阱和getPrototypeOf陷阱可以拦截这两个方法的执行过程。

setPrototypeOf陷阱接受2个参数:

  • trapTarget

    接受原型设置的对象(代理的目标)
  • proto

    作为原型使用的对象

传入Object.setPrototypeOf()方法和Reflect.setPrototypeOf()方法的均是以上两个参数。

getPrototypeOf陷阱、Object.getPrototypeOf()方法和Reflect.getPrototypeOf()方法只接受参数trapTarget。

原型代理陷阱的运行机制

原型代理陷阱有一些限制:

  1. getPrototypeOf陷阱必须返回对象或null

  2. 在setPrototypeOf陷阱中,如果操作失败则返回的一定是false,此时Object.setPrototypeOf()会抛出错误,如果setPrototypeOf返回了任何不是false的值,那么Object.setPrototypeOf()便假设操作成功。

let target = {};
let proxy = new Proxy(target, {
getPrototypeOf(trapTarget) {
return null;
},
setPrototypeOf(trapTarget, proto) {
return false;
}
}); let targetProto = Object.getPrototypeOf(target);
let proxyProto = Object.getPrototypeOf(proxy); console.log(targetProto === Object.prototype); // true
console.log(proxyProto === Object.prototype); // false
console.log(proxyProto); // null // 成功
Object.setPrototypeOf(target, {}); // 给不存在的属性赋值会抛出错误:
// Uncaught TypeError: 'setPrototypeOf' on proxy: trap returned falsish
Object.setPrototypeOf(proxy, {});

可以使用Reflect上的对应方法实现这两个陷阱的默认行为。

let target = {};
let proxy = new Proxy(target, {
getPrototypeOf(trapTarget) {
return Reflect.getPrototypeOf(trapTarget);
},
setPrototypeOf(trapTarget, proto) {
return Reflect.setPrototypeOf(trapTarget, proto);
}
}); let targetProto = Object.getPrototypeOf(target);
let proxyProto = Object.getPrototypeOf(proxy); console.log(targetProto === Object.prototype); // true
console.log(proxyProto === Object.prototype); // true // 成功
Object.setPrototypeOf(target, {}); // 成功:
Object.setPrototypeOf(proxy, {});

对象可扩展性陷阱

ECMAScript 5 已经通过 Object.preventExtensions() 方法和 Object.isExtensible() 方法修正了对象的可扩展性;

ECMAScript 6 可以通过代理中的 preventExtensions 和 isExtensible 陷阱拦截这两个方法并调用底层对象。

两个陷阱都接受唯一参数 trapTarget 对象,并调用它上面的方法。

isExtensible 陷阱返回的一定是一个 boolean 值,表示对象是否可扩展;

preventExtensions 陷阱返回的也一定是布尔值,表示操作是否成功。

Reflect.preventExtensions() 方法和 Reflect.isExtensible() 方法实现了相应陷阱中的默认行为,二两都返回布尔值。

两个基础示例

默认实现:

let target = {};
let proxy = new Proxy(target, {
isExtensible(trapTarget) {
return Reflect.isExtensible(trapTarget);
},
preventExtensions(trapTarget) {
return Reflect.preventExtensions(trapTarget);
}
}); console.log(Object.isExtensible(target)); // true
console.log(Object.isExtensible(proxy)); // true Object.preventExtensions(proxy); console.log(Object.isExtensible(target)); // false
console.log(Object.isExtensible(proxy)); // false

使用陷阱使 Object.preventExtensions() 对 proxy 失效。

let target = {};
let proxy = new Proxy(target, {
isExtensible(trapTarget) {
return Reflect.isExtensible(trapTarget);
},
preventExtensions(trapTarget) {
return false;
}
}); console.log(Object.isExtensible(target)); // true
console.log(Object.isExtensible(proxy)); // true // 抛出错误:
// Uncaught TypeError: 'preventExtensions' on proxy: trap returned falsish
Object.preventExtensions(proxy); console.log(Object.isExtensible(target)); // true
console.log(Object.isExtensible(proxy)); // true

属性描述符陷阱

ECMAScript 5 最重要的特性之一是可以使用 Object.defineProperty() 方法定义属性特性(property attribute)。可以通过 Object.getOwnPropertyDescriptor() 方法来获取这些属性。

在代理中可以分别用 defineProperty 陷阱和 getOwnPropertyDescriptor 陷阱拦截 Object.defineProperty() 方法和 Object.getOwnPropertyDescriptor() 方法的调用。

defineProperty 陷阱接受以下参数:

  • trapTarget

    要定义属性的对象(代理的目标)
  • key

    属性的键(字符串或Symbol)
  • descriptor

    属性的描述符对象

操作成功后返回 true,否则返回 false。

getOwnPropertyDescriptor 陷阱接受以下参数:

  • trapTarget

    要定义属性的对象(代理的目标)
  • key

    属性的键(字符串或Symbol)

最终返回描述符。

陷阱默认行为示例:

let proxy = new Proxy({}, {
defineProperty(trapTarget, key, descriptor) {
return Reflect.defineProperty(trapTarget, key, descriptor);
},
getOwnPropertyDescriptor(trapTarget, key) {
return Reflect.getOwnPropertyDescriptor(trapTarget, key);
}
}); Object.defineProperty(proxy, "name", {
value: "proxy"
}); console.log(proxy.name); // "proxy" let descriptor = Object.getOwnPropertyDescriptor(proxy, "name"); console.log(descriptor.value); // "proxy"

给 Object.defineProperty() 添加限制

defineProperty 陷阱返回布尔值来表示操作是否成功。

返回 true 时,Object.defineProperty() 方法成功执行;

返回 false 时, Object.defineProperty() 方法抛出错误。

例:阻止 Symbol 类型的属性

let proxy = new Proxy({}, {
defineProperty(trapTarget, key, descriptor) {
if (typeof key === "symbol") {
return false;
} return Reflect.defineProperty(trapTarget, key, descriptor);
}
}); Object.defineProperty(proxy, "name", {
value: "proxy"
}); console.log(proxy.name); // "proxy" let nameSymbol = Symbol("name"); // 抛出错误:
// Uncaught TypeError: 'defineProperty' on proxy: trap returned falsish for property 'Symbol(name)'
Object.defineProperty(proxy, nameSymbol, {
value: "proxy"
});

描述符对象限制

defineProperty 陷阱

defineProperty 陷阱的描述对象已规范化,只有下列属性会被传递给 defineProperty 陷阱的描述符对象。

  • enumerable
  • configurable
  • value
  • writable
  • get
  • set
let proxy = new Proxy({}, {
defineProperty(trapTarget, key, descriptor) {
console.log(descriptor.value); // "proxy"
console.log(descriptor.name); // undefined return Reflect.defineProperty(trapTarget, key, descriptor);
}
}); Object.defineProperty(proxy, "name", {
value: "proxy",
name: "custom"
});

getOwnPropertyDescriptor 陷阱

getOwnPropertyDescriptor 陷阱的返回值必须是 null、undefined或一个对象;

如果返回对象,则对象自己的属性只能是 enumerable、configurable、vlaue、writable、get和set;

在返回的对象中使用不被允许的属性会抛出一个错误。

let proxy = new Proxy({}, {
getOwnPropertyDescriptor(trapTarget, key) {
return {
name: "proxy"
};
}
}); // 给不存在的属性赋值会抛出错误
// Uncaught TypeError: 'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property 'name' which is either non-existant or configurable in the proxy target
let descriptor = Object.getOwnPropertyDescriptor(proxy, "name");

这条限制可以确保无论代理中使用了什么方法,Object.getOwnPropertyDescriptor() 返回值的结构总是可靠的。

ownKeys 陷阱

ownKeys 陷阱可以拦截内部方法 [[OwnPropertyKeys]],通过返回一个数组的值可以覆写其行为。

这个数组被用于 Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols() 和 Object.assign() 4个方法,Object.assign() 方法用数组来确定需要复制的属性。

ownKeys 陷阱通过 Reflect.ownKeys() 方法实现默认的行为,返回的数组中包含所有自有属性的键名,字符串类型和 Symbol 类型的都包含在内。

Object.getOwnPropertyNames() 方法和 Object.keys() 方法返回的结果将 Symbol 类型的属性名排除在外;

Object.getOwnPropertySymbols() 方法返回的结果将字符串类型的属性名排除在外;

Object.assign() 方法支持字符串和 Symbol 两种类型。

ownKeys 陷阱唯一接受的参数时操作的目标,返回值必须是一个数组或类数组对象,否则就抛出错误。

例:过滤任何以下划线字符开头的属性名称。

let proxy = new Proxy({}, {
ownKeys(trapTarget) {
return Reflect.ownKeys(trapTarget).filter(key => {
return typeof key !== "string" || key[0] !== "_";
});
}
}); let nameSymbol = Symbol("name"); proxy.name = "proxy";
proxy._name = "private";
proxy[nameSymbol] = "symbol"; let names = Object.getOwnPropertyNames(proxy),
keys = Object.keys(proxy),
symbols = Object.getOwnPropertySymbols(proxy); console.log(names.length); // 1
console.log(names[0]); // "name" console.log(keys.length); // 1
console.log(keys[0]); // "name" console.log(symbols.length); // 1
console.log(symbols[0]); // "Symbol(name)"

函数代理的 apply 和 construct 陷阱

所有代理陷阱中,只有 apply 和 construct 的代理目标是一个函数。

函数有两个内部方法 [[Call]] 和 [[Construct]],apply 陷阱和 construct 陷阱可以覆写这些内部方法。

若使用 new 操作符调用函数,则执行 [[Construct]] 方法;若不用,则执行 [[Call]] 方法。

apply 陷阱和 Reflect.apply() 都接受以下参数:

  • trapTarget

    被执行的函数(代理的目标)
  • thisArg

    函数被调用时内部this的值
  • argumentList

    传递给函数的参数数组

当使用 new 调用函数时调用的 construct 陷阱接受以下参数:

  • trapTarget

    被执行的函数(代理的目标)
  • argumentList

    传递给函数的参数数组

Reflect.construct() 方法也接受这两个参数,其还有一个可选的第三个参数 newTarget。

let target = function() { return 42; },
proxy = new Proxy(target, {
apply: function(trapTarget, thisArg, argumentList) {
return Reflect.apply(trapTarget, thisArg, argumentList);
},
construct: function(trapTarget, argumentList) {
return Reflect.construct(trapTarget, argumentList);
}
}); // 一个目标是函数的代理开起来也像一个函数
console.log(typeof proxy); // function
console.log(proxy()); // 42
var instance = new proxy();
console.log(instance instanceof proxy); // true
console.log(instance instanceof target); // true

验证函数参数

例:验证所有参数必须是数字:

// 将所有参数相加
function sum(...values) {
return values.reduce((previous, current) => previous + current, 0);
} let sumProxy = new Proxy(sum, {
apply: function(trapTarget, thisArg, argumentList) {
argumentList.forEach((arg) => {
if (typeof arg !== "number") {
throw new TypeError("所有参数必须是数字");
}
}); return Reflect.apply(trapTarget, thisArg, argumentList);
},
construct: function(trapTarget, argumentList) {
throw new TypeError("该函数不可通过new来调用");
}
}); console.log(sumProxy(1, 2, 3, 4)); // 10 // 抛出错误
// Uncaught TypeError: 所有参数必须是数字
console.log(sumProxy(1, "2", 3, 4)); // 抛出错误
// Uncaught TypeError: 该函数不可通过new来调用
let result = new sumProxy();

可调用的类构造函数

使用 apply 陷阱创建实例

class Person {
constructor(name) {
this.name = name;
}
} let PersonProxy = new Proxy(Person, {
apply: function(trapTarget, thisArg, argumentList) {
return new trapTarget(...argumentList);
}
}); let me = PersonProxy("JiaJia");
console.log(me.name); // JiaJia
console.log(me instanceof Person); // true
console.log(me instanceof PersonProxy); // true

可撤销代理

可以使用 Proxy.revocable() 方法创建可撤销的代理,该方法采用与 Proxy 构造函数相同的参数:目标对象和代理处理程序。

返回值是具有以下属性的对象:

  • proxy

    可被撤销的代理对象
  • revoke

    撤销代理要调用的函数
let target = {
name: "target"
}; let { proxy, revoke } = Proxy.revocable(target, {}); console.log(proxy.name); // target revoke(); // 抛出错误
// Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
console.log(proxy.name);

解决数组问题

数组问题

let colors = ["red", "green", "blue"];

console.log(colors.length); // 3

colors[3] = "black";

console.log(colors.length); // 4
console.log(colors[3]); // black colors.length = 2;

自定义数组类型

function toUint32(value) {
return Math.floor(Math.abs(Number(value))) % Math.pow(2, 32);
} function isArrayIndex(key) {
let numericKey = toUint32(key);
return String(numericKey) == key && numericKey < (Math.pow(2, 32) - 1);
} class MyArray {
constructor(length = 0) {
this.length = length;
return new Proxy(this, {
set(trapTarget, key, value) {
let currentLength = Reflect.get(trapTarget, "length"); if (isArrayIndex(key)) {
let numericKey = Number(key);
if (numericKey >= currentLength) {
Reflect.set(trapTarget, "length", numericKey + 1);
}
} else if (key === "length") {
if (value < currentLength) {
for (let index = currentLength - 1; index >= value; index--) {
Reflect.deleteProperty(trapTarget, index);
}
}
} return Reflect.set(trapTarget, key, value);
}
});
}
} let colors = new MyArray(3);
console.log(colors instanceof MyArray); // true
console.log(colors.length); // 3 let colors2 = new MyArray(5);
console.log(colors.length); // 3
console.log(colors2.length); // 5 colors[0] = "red";
colors[1] = "green";
colors[2] = "blue";
colors[3] = "black"; console.log(colors.length); // 4 colors.length = 2;
console.log(colors.length); // 2
console.log(colors[3]); // undefined
console.log(colors[2]); // undefined
console.log(colors[1]); // "green"
console.log(colors[0]); // "red"

将代理作为原型

let target = {};
let proxy = new Proxy(target, {
defineProperty(trapTarget, name, descriptor) {
return false;
}
});
let newTarget = Object.create(proxy); Object.defineProperty(newTarget, "name", {
value: "newTarget"
}); console.log(newTarget.name); // "newTarget"
console.log(newTarget.hasOwnProperty("name")); // true
console.log(Object.getPrototypeOf(newTarget) === proxy); // true

关于 Object.create() 方法可以参照 这里

上例中 newTarget 的原型是代理,但是在对象上定义属性的操作不需要操作对象原型,所以没有触发代理中的陷阱。

尽管代理作为原型使用时及其受限,但有几个陷阱仍然有用。

在原型上使用 get 陷阱

let target = {};
let thing = Object.create(new Proxy(target, {
get(trapTarget, key, value) {
throw new ReferenceError(`${key} deesn't exist`);
}
})); thing.name = "thing";
console.log(thing.name); // "thing" // 抛出异常:
// Uncaught ReferenceError: unknown deesn't exist
let unknown = thing.unknown;

访问对象上不存在的属性时,会触发原型中的 get 陷阱。

在原型上使用 set 陷阱

let target = {};
let thing = Object.create(new Proxy(target, {
set (trapTarget, key, value, receiver) {
return Reflect.set(trapTarget, key, value, receiver);
}
})); console.log(thing.hasOwnProperty("name")); // false // 触发set陷阱
thing.name = "thing";
console.log(thing.name); // "thing"
console.log(thing.hasOwnProperty("name")); // true // 不触发set陷阱
thing.name = "boo";
console.log(thing.name); // "boo"

在原型上使用 has 陷阱

let target = {}
let thing = Object.create(new Proxy(target, {
has (trapTarget, key) {
return Reflect.has(trapTarget, key);
}
})); // 触发 has 陷阱
console.log("name" in thing); // false
thing.name = "thing";
// 不触发 has 陷阱
console.log("name" in thing); // true

第一次 in 操作符触发 has 陷阱,是因为 name 不是 thing 的自有属性。

将代理用作类的原型

function NoSuchProperty() {

}

let proxy = new Proxy({}, {
get (trapTarget, key, receiver) {
throw new ReferenceError(`${key} doesn't exist`);
}
}); NoSuchProperty.prototype = proxy; class Square extends NoSuchProperty {
constructor(length, width) {
super();
this.length = length;
this.width = width;
}
} let shape = new Square(2, 6); let shapeProto = Object.getPrototypeOf(shape);
console.log(shapeProto === proxy); // false let secondLevelProto = Object.getPrototypeOf(shapeProto);
console.log(secondLevelProto === proxy); // true let area1 = shape.length * shape.width;
console.log(area1); // 12 // 由于 wdth 不存在,抛出错误:
// Uncaught ReferenceError: wdth doesn't exist
let area2 = shape.length * shape.wdth;

【读书笔记】【深入理解ES6】#12-代理(Proxy)和反射(Reflection)API的更多相关文章

  1. 代理(Proxy)和反射(Reflection)

    前面的话 ES5和ES6致力于为开发者提供JS已有却不可调用的功能.例如在ES5出现以前,JS环境中的对象包含许多不可枚举和不可写的属性,但开发者不能定义自己的不可枚举或不可写属性,于是ES5引入了O ...

  2. 20150206读书笔记<深入理解计算机系统>

    ●第一章 C是系统级编程的首选.C++显示支持抽象,属于应用级程序设计语言. 简单例子: 一个典型系统的硬件组成: 存储器的层次结构: 注:存储器层次结构的设计思想是,该层存储器作为下一层存储器的高速 ...

  3. 深入理解ES6之——代理和反射(proxy)

    通过调用new proxy()你可以创建一个代理来替代另一个对象(被称为目标),这个代理对目标对象进行了虚拟,因此该代理与该目标对象表面上可以被当做同一个对象来对待. 创建一个简单的代理 当你使用Pr ...

  4. CSharp设计模式读书笔记(13):代理模式(学习难度:★★★☆☆,使用频率:★★★★☆)

    代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问. 模式角色与结构: 示例代码: using System; using System.Collections.Generi ...

  5. python 进阶读书笔记1 -- 理解python一切皆对象

    理解python一切皆对象: 1.所有的类都是由type创建的 2.所有的类的基类都是object 3.type是类,也是实例,type的基类是object,type对象是由type创建的 4.obj ...

  6. 【读书笔记::深入理解linux内核】内存寻址【转】

    转自:http://www.cnblogs.com/likeyiyy/p/3837272.html 我对linux高端内存的错误理解都是从这篇文章得来的,这篇文章里讲的 物理地址 = 逻辑地址 – 0 ...

  7. 【读书笔记::深入理解linux内核】内存寻址

    我对linux高端内存的错误理解都是从这篇文章得来的,这篇文章里讲的 物理地址 = 逻辑地址 – 0xC0000000:这是内核地址空间的地址转换关系. 这句话瞬间让我惊呆了,根据我的CPU的知识,开 ...

  8. 20150207读书笔记<深入理解计算机系统2-1>

    第二章 信息存储 (1)  多数计算机以一个字节作为最小可寻址的存储器单元. 机器级程序将存储器看成一个非常大的字节数组,称为虚拟存储器. 存储器的每个字节都由唯一的数字标识,称为它的地址. 所有可能 ...

  9. 读书笔记 effective c++ Item 12 拷贝对象的所有部分

    1.默认构造函数介绍 在设计良好的面向对象系统中,会将对象的内部进行封装,只有两个函数可以拷贝对象:这两个函数分别叫做拷贝构造函数和拷贝赋值运算符.我们把这两个函数统一叫做拷贝函数.从Item5中,我 ...

  10. 《Linux命令行与shell脚本编程大全》- 读书笔记3 - 理解shell

    当用户登录终端的时候,通常会启动一个默认的交互式shell.系统究竟启动哪个shell,这取决于用户配置.一般这个shell都是/bin/shell.默认的系统shell(/bin/sh)用于系统sh ...

随机推荐

  1. PHP中被忽略的性能优化利器:生成器

    如果是做Python或者其他语言的小伙伴,对于生成器应该不陌生.但很多PHP开发者或许都不知道生成器这个功能,可能是因为生成器是PHP 5.5.0才引入的功能,也可以是生成器作用不是很明显.但是,生成 ...

  2. 关于scrapy的piplines

    1.进入setting中把ITEM_piplines文件注销去掉 2.在piplines中写好代码 # -*- coding: utf- -*- # Define your item pipeline ...

  3. PHP csv文件内容转成数组/Json

    $lines = array_map('str_getcsv', file($filePath));; $result = array(); $headers = null; if (count($l ...

  4. 为什么还坚持.NET? 找一门适合自己的语言去做编程

    为什么还坚持.NET? 找一门适合自己的语言去做编程 接触了.NET快十二年了,现在专注于分布式服务的开发. 中间经历过各种编程语言的诱惑,ios等. 前几年才对自己有比较明确的定位 技术上:找到适合 ...

  5. python爬虫小结1

    先看正则化,正则化就是描述命令和字符切分.查找.筛选等功能的方便方式. http://www.cnblogs.com/fnng/archive/2013/05/20/3089816.html 一个游戏 ...

  6. GitLab配置ssh key

    一.背景 当前很多公司都选择git作为代码版本控制工具,然后自己公司搭建私有的gitlab来管理代码,我们在clone代码的时候可以选择http协议,当然我们亦可以选择ssh协议来拉取代码.但是网上很 ...

  7. java 设计模式-缺省适配器模式

    本文转载地址:http://www.cnblogs.com/iyangyuan/archive/2013/03/11/2954808.html 在程序设计过程中,读者很可能遇到这样一种困境:设计了一个 ...

  8. coursera 视频总是缓冲或者无法观看的解决办法

    注意!!!该方法针对Windows用户,亲测有效. 1.用管理员权限记事本打开host文件 2.将如下内容复制到文件末尾 52.84.246.90 d3c33hcgiwev3.cloudfront.n ...

  9. Git常用命令清单笔记

    git github 小弟调调 2015年01月12日发布 赞  |   6收藏  |  45 5k 次浏览 这里是我的笔记,记录一些git常用和一些记不住的命令,这个笔记原本是基于 颜海镜的文章增加 ...

  10. Docker(十三):OpenStack部署Docker集群

    1.介绍 本教程使用Compose.Machine.Swarm工具把WordPress部署在OpenStack上. 本节采用Consul作为Swarm的Discovery Service模块,要利用C ...