【译】《Understanding ECMAScript6》- 第三章-Object

目录

  • Object分类
  • Object字面量扩展
  • Object.assign()
  • 重复属性
  • 改变原型
  • super引用
  • 方法
  • 总结

ES6针对Object的改进,旨在使JavaScript语言更加接近“万物皆对象”的理念。随着越来越多地使用Object类型进行开发,开发者们越来越不满足于Object相对低下的开发效率。

ES6通过多种途径对Object进行了改进,包括语法的调整、以及新的操作和交互方式等。

Object分类

JavaScript中的Object有很多不同的类别,比如自定义的对象和语言内置的对象,很容易产生混淆。为了更精确地区分不同类别的对象,ES6引入了几个新的术语,这些术语将Object的类别具体为以下几种;

  • 普通对象(Ordinary objects)是指具备JavaScript对象所有默认行为的对象;
  • 怪异对象(Exotic objects)是指某些行为与默认行为不同的对象;
  • 标准对象(Standard objects)是指由ES6语言规范定义的对象,比如Array、Date等。标准对象可以是普通对象,也可以是怪异对象
  • 内置对象(Built-in objects)是指JavaScript运行环境定义的对象。所有的标准对象都是内置对象

本书将在后续的内容中详细讲述每种对象的具体细节。

Object字面量扩展

Object字面量表达式被广泛使用于JavaScript程序中,几乎所有的JavaScript应用程序中都可以找到这种模式。Object字面量有着类似JSON的简洁语法,这是它之所以如此流行的主要原因。ES6对Object字面量语法进行了扩展,保持语法简洁的前提下,也增强了功能性。

属性初始化的缩写模式

在ES5及其之前的版本中,Object字面量必须写成键值对的格式,这就意味着,在某些场景下会产生一些重复的语句,如下:

function createPerson(name, age) {return {name: name,age: age};
}

上述代码中的createPerson()函数创建了一个对象,这个对象的属性值与createPerson()的实参值相同。这会令许多开发者误认为对象的值是createPerson()实参的副本。

ES6中新增了Object字面量的简洁声明语法,可以一定程度上消除以上误解。如果对象的某个属性与一个本地变量同名,就可以在声明对象时只写这个属性的key,省略冒号和value。如下:

function createPerson(name, age) {return {name,age};
}

如果Object字面量的某个属性只有key没有value,JavaScript引擎便会在当前作用域内搜索是否存在与key同名的变量。如果存在,则将同名变量的值赋值为key对应的value。上述代码中的name属性对应的value就是本地变量name的值。

ES6新增这种机制的目的是令Object字面量语法更加简洁化。使用本地变量的值作为对象属性的value是一种很常见的模式,初始化属性的缩写模式可以令代码更加简洁。

函数初始化的缩写模式

ES6同样精简了对象内函数的声明语法。在ES6之前,开发者必须按照键值对的格式声明对象内的函数,如下:

var person = {name: "Nicholas",sayName: function() {console.log(this.name);}
};

ES6中,可以省略冒号和function关键字,如下:

var person = {name: "Nicholas",sayName() {console.log(this.name);}
};

使用上述代码中的缩写模式声明的函数与上例中的作用完全相同。

计算属性名

JavaScript允许使用方括号计算对象的属性名,一方面令对象属性的操作更加动态化,另一方面避免了不能使用.直接访问的属性名引起的语法错误。如下:

var person = {},lastName = "last name";
person["first name"] = "Nicholas";
person[lastName] = "Zakas";
console.log(person["first name"]);      // "Nicholas"
console.log(person[lastName]);          // "Zakas"

上述代码中的person对象的两个属性名first namelast name都包含空格,无法直接使用.访问,只能通过方括号访问。方括号内可以包括字符串和变量。

ES5中可以使用字符串作为对象的属性名:

var person = {"first name": "Nicholas"
};
console.log(person["first name"]);      // "Nicholas"

使用字符串作为对象属性名的前提是在声明之前必须明确知道此字符串的值。如果此字符串并非固定值,比如需要根据变量值动态计算,这种场景下便不能使用上述的声明方式了。

为满足以上需求,ES6将方括号计算属性名的机制引入了Object字面量,如下:

var lastName = "last name";
var person = {"first name": "Nicholas",[lastName]: "Zakas"
};
console.log(person["first name"]);      // "Nicholas"
console.log(person[lastName]);          // "Zakas"

上述代码中,Object字面量内的方括号的作用是计算对象的属性名,其内部为字符串运算。你同样可以使用以下模式:

var suffix = " name";
var person = {["first" + suffix]: "Nicholas",["last" + suffix]: "Zakas"
};
console.log(person["first name"]);      // "Nicholas"
console.log(person["last name"]);       // "Zakas"

ES6中,方括号不仅可以在访问对象属性时计算属性名,同样可以在对象声明时计算属性名。

Object.assign()

mixin是组合对象常用的模式之一,本质是将一个对象的属性键值对克隆给另一个对象。很多JavaScript类库有类似如下的mixin函数:

function mixin(receiver, supplier) {Object.keys(supplier).forEach(function(key) {receiver[key] = supplier[key];});return receiver;
}

上述代码中的mixin()函数将supplier对象的自有属性(不包括原型链属性)克隆赋值给receiver对象。这种方式可以不通过继承实现receiver属性的扩展。请看如下示例:

function EventTarget() { /*...*/ }
EventTarget.prototype = {constructor: EventTarget,emit: function() { /*...*/ },on: function() { /*...*/ }
};
var myObject = {};
mixin(myObject, EventTarget.prototype);
myObject.emit("somethingChanged");

上述代码中,myObject对象通过克隆EventTarget.prototype的属性获取到了打印事件以及使用emit()on()函数的功能。

ES6新增的Object.assign()进一步加强了这种模式,并且更加语义化。上文提到的mixin()函数使用赋值运算符=进行属性克隆,这样的缺点是无法处理对象的存储器属性(后续章节详细讲述)。Object.assign()解决了这一问题。

不同的JavaScript类库实现mixin模式的函数取名迥异,其中extend()mix()是使用面很广泛的函数名。ES6初期除了Object.assign()以外,还曾引入了Object.mixin()方法。Object.mixin()可以克隆对象的存储器属性,但是由于super的引入(后续章节详细讲述),最终取消了Object.mixin()的使用。

Object.assign()可以取代上文提到的mixin()函数:

function EventTarget() { /*...*/ }
EventTarget.prototype = {constructor: EventTarget,emit: function() { /*...*/ },on: function() { /*...*/ }
}
var myObject = {}
Object.assign(myObject, EventTarget.prototype);
myObject.emit("somethingChanged");

Object.assign()可以接受任意数目的克隆源对象,克隆目标对象按照克隆源对象的顺序依次克隆。也就是说,队列后面的源对象属性会覆盖它前面的源对象同名属性。如下:

var receiver = {};
Object.assign(receiver, {type: "js",name: "file.js"}, {type: "css"}
);
console.log(receiver.type);     // "css"
console.log(receiver.name);     // "file.js"

上述代码最终的receiver.type值为css,因为第二个源对象覆盖了第一个源对象的同名属性。

Object.assign()对于ES6来说,并不是一个革命性的功能,但是它规范了mixin模式,而不必依赖于第三方类库。

存储器属性的处理

mixin模式下存储器属性是不能被完全克隆的,Object.assign()本质上是通过赋值运算符克隆属性,在处理存储器属性时,将源对象的存储器属性的运算结果克隆至目标对象。如下:

var receiver = {},supplier = {get name() {return "file.js"}};
Object.assign(receiver, supplier);
var descriptor = Object.getOwnPropertyDescriptor(receiver, "name");
console.log(descriptor.value);      // "file.js"
console.log(descriptor.get);        // undefined

上述代码中的源对象supplier有一个存储器属性name。使用Object.assign()之后,receiver被赋予一个值为"filter.js"的常规属性receiver.name。这是由于 supplier.name的运算结果为"filter.js"Object.assign()将运算结果克隆为receiver的一个常规属性。

重复属性

ES5严格模式下不允许Object字面量存在key值重复的属性,比如:

var person = {name: "Nicholas",name: "Greg"        // syntax error in ES5 strict mode
};

上述代码在ES5严格模式下会抛出语法错误。

ES6移除了重复属性的语法错误。不论是在非严格模式还是严格模式下,上例中的代码都不会抛错,而且后面的name属性值将覆盖前面的值。

var person = {name: "Nicholas",name: "Greg"        // not an error in ES6
};
console.log(person.name);       // "Greg"

上述代码person.name的取值为"Greg",因为name属性取的是最后一次赋值。

改变原型

原型是JavaScript实现继承的基础,ES6进一步加强了原型的作用。ES5中提供Object.getPrototypeOf()函数用来获取指定对象的原型。ES6引入了其逆向操作函数Object.setPrototypeOf()用来改变指定对象的原型。

不论是使用构造函数还是通过Object.create()创建的对象,它们的原型在创建的时候就被指定了。在ES6之前,并没有规范的方法改变对象的原型。Object.setPrototypeOf()打破了对象被创建后不能更改原型的规范,在此意义上,可以说Object.setPrototypeOf()是革命性的。

Object.setPrototypeOf()接收两个参数,第一个参数是被更改原型的对象,第二个参数是第一个参数被更改后的原型。如下:

let person = {getGreeting() {return "Hello";}
};
let dog = {getGreeting() {return "Woof";}
};
// prototype is person
let friend = Object.create(person);
console.log(friend.getGreeting());                      // "Hello"
console.log(Object.getPrototypeOf(friend) === person);  // true
// set prototype to dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting());                      // "Woof"
console.log(Object.getPrototypeOf(friend) === dog);     // true

上述代码中定义了两个基础对象:persondog。两者都有一个getGreeting()函数。对象friend创建时继承自person,此时friend.getGreeting()的运算结果为”Hello“。然后通过Object.setPrototypeOf()函数将friend对象的原型更改为dog,此时friend.getGreeting()的运算结果为”woof“

一个对象的原型储存在内部隐藏属性[[Prototype]]中。Object.getPrototypeOf()函数返回[[Prototype]]的值,Object.setPrototypeOf()则是将[[Prototype]]的值改变为指定的value。除了上述两个函数以外,还有一些其他途径对[[Prototype]]进行操作。

在ES5之前就已经有少数JavaScript引擎实现了通过操作__proto__属性来获取和更改对象原型的方法。可以说__proto__Object.getPrototypeOf()Object.setPrototypeOf()的先行者。但是并非所有的JavaScript引擎都支持__proto__,所以ES6对此进行了规范。

在ES6中,Object.prototype.__proto__作为一个存储器属性,它的get方法为Object.getPrototypeOf()set方法为Object.setPrototypeOf()。所以,使用Object.getPrototypeOf()Object.setPrototypeOf()函数与直接操作__proto__本质上是相同的。如下:

let person = {getGreeting() {return "Hello";}
};
let dog = {getGreeting() {return "Woof";}
};
// prototype is person
let friend = {__proto__: person
};
console.log(friend.getGreeting());                      // "Hello"
console.log(Object.getPrototypeOf(friend) === person);  // true
console.log(friend.__proto__ === person);               // true
// set prototype to dog
friend.__proto__ = dog;
console.log(friend.getGreeting());                      // "Woof"
console.log(friend.__proto__ === dog);                  // true
console.log(Object.getPrototypeOf(friend) === dog);     // true

上述代码与前例的功能相同。唯一的区别是用Object字面量配合__proto__属性取代了Object.create()

__proto__属性有以下特性:

  1. 使用Object字面量声明时,__proto__属性只能被赋值一次。重复赋值会引起错误。__proto__是ES6中Object字面量中唯一有次限制的属性。

  2. 使用["__proto__"]访问对象属性时,方括号内的字符串只能作为一个常规的属性key,并不能操作__proto__属性。它是不适用于方括号计算对象属性key规则的少数属性之一。

操作__proto__属性时要时刻谨记上述规则。

super引用

如前文所述,原型是JavaScript中非常重要的环节,ES6针对原型新增了很多强化功能,super引用便是其中之一。举个例子,在ES5环境下,如果想复写一个对象原型的同名函数,你可能会选择类似下述代码的方式:

let person = {getGreeting() {return "Hello";}
};
let dog = {getGreeting() {return "Woof";}
};
// prototype is person
let friend = {__proto__: person,getGreeting() {// same as this.__proto__.getGreeting.call(this)return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";}
};
console.log(friend.getGreeting());                      // "Hello, hi!"
console.log(Object.getPrototypeOf(friend) === person);  // true
console.log(friend.__proto__ === person);               // true
// set prototype to dog
friend.__proto__ = dog;
console.log(friend.getGreeting());                      // "Woof, hi!"
console.log(friend.__proto__ === dog);                  // true
console.log(Object.getPrototypeOf(friend) === dog);     // true

上述代码中,friend有一个与原型链中重名的getGreeting()。通过Object.getPrototypeOf()调用其原型的同名函数后追加字符串", hi!".call(this)确保原型函数中的作用域为friend

需要注意的是,Object.getPrototypeOf().call(this)必须配合使用。遗漏任何一方都可能引起问题。因此,ES6引入了super以简化这种操作。

简单来讲,super可以理解为一个指向当前对象原型的指针,等价于Object.getPrototypeOf(this)。所以,前例中的代码可以改写为以下形式:

let friend = {__proto__: person,getGreeting() {// in the previous example, this is the same as:// 1. Object.getPrototypeOf(this).getGreeting.call(this)// 2. this.__proto__.getGreeting.call(this)return super.getGreeting() + ", hi!";}
};

上述代码中的super.getGreeting()等价于Object.getPrototypeOf(this).getGreeting.call(this) 或者this.__proto__.getGreeting.call(this)

super的功能并不仅限于此。比如在多重继承的场景下,Object.getPrototypeOf()并不能满足需求,如下:

let person = {getGreeting() {return "Hello";}
};// prototype is person
let friend = {__proto__: person,getGreeting() {return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";}
};// prototype is friend
let relative = {__proto__: friend
};console.log(person.getGreeting());                  // "Hello"
console.log(friend.getGreeting());                  // "Hello, hi!"
console.log(relative.getGreeting());                // error!

上述代码中,执行relative.getGreeting()Object.getPrototypeOf()会报错。因为此时的this指向的是relativerelative的原型为friend。所以当friend.getGreeting().call()this被指定为relative时相当于执行relative.getGreeting(),形成了无限递归的死循环,直到堆栈溢出。使用super可以很轻易的化解这种问题。如下:

let person = {getGreeting() {return "Hello";}
};
// prototype is person
let friend = {__proto__: person,getGreeting() {return super.getGreeting() + ", hi!";}
};
// prototype is friend
let relative = {__proto__: friend
};
console.log(person.getGreeting());                  // "Hello"
console.log(friend.getGreeting());                  // "Hello, hi!"
console.log(relative.getGreeting());                // "Hello, hi!"

super的指向是固定的。不论有多少层继承关系,super.getGreeting()永远指向person.getGreeting()

super只能在对象方法中使用,不能在常规函数和全局作用域内使用,否则会抛出语法错误

方法

在ES6之前的版本中,方法并没有准确的定义。通常认为方法是一种函数类型的对象属性。ES6正式规范了方法的定义,作为方法的函数有一个内部属性[[HomeObject]]表明方法的归属对象:

let person = {// 方法getGreeting() {return "Hello";}
};
// 不是方法
function shareGreeting() {return "Hi!";
}

上述代码中,person对象有一个方法getGreeting()getGreeting()的内部属性[[HomeObject]]值为person,表明它的归属对象是personshareGreeting()是一个函数,它没有[[HomeObject]]属性,因为它不是任何对象的方法。大多数场景下,方法与函数的区别并不是很重要,但是使用super时需要谨慎处理两者的异同。

super引用是根据[[HomeObject]]属性来决定其指向的。其内部机制如下:

  1. 首先根据被调用方法的[[HomeObject]]属性值(即当前方法的归属对象),通过Object.getPrototypeOf()获取原型;
  2. 获取到原型后,检索同名方法;
  3. 绑定this指向并执行方法函数。

如果一个函数没有[[HomeObject]]属性或者属性值是错误的,以上的机制就无法运行。如下:

let person = {getGreeting() {return "Hello";}
};
// prototype is person
let friend = {__proto__: person,getGreeting() {return super() + ", hi!";}
};
function getGlobalGreeting() {return super.getGreeting() + ", yo!";
}
console.log(friend.getGreeting());  // "Hello, hi!"
getGlobalGreeting();                      // throws error

上述代码中的friend.getGreeting()返回了正确结果,而getGlobalGreeting()运行产生了错误,因为super并不能在常规函数内使用。由于getGlobalGreeting()函数不存在[[HomeObject]]属性,所以不能通过super向上检索。即便getGlobalGreeting()函数被动态的赋值给对象的方法,它仍然不能使用super。如下:

// prototype is person
let friend = {__proto__: person,getGreeting() {return super() + ", hi!";}
};
function getGlobalGreeting() {return super.getGreeting() + ", yo!";
}
console.log(friend.getGreeting());  // "Hello, hi!"
// assign getGreeting to the global function
friend.getGreeting = getGlobalGreeting;
friend.getGreeting();               // throws error

上述代码中,全局函数getGlobalGreeting()被动态地赋值给friendgetGreeting()方法。随后调用friend.getGreeting()产生和前例相同的错误。这是因为[[HomeObject]]的属性值在函数/方法被创建的时候就固定了,随后不能被改变。

总结

Object是JavaScript语言中至关重要的模块,ES6在简化操作和强化功能方面进行了许多改进。

在Object字面量方面,属性初始化的缩写模式可以更加简洁地通过当前作用域的同名变量进行赋值;计算属性名为对象扩展属性提供更多的动态化支持;函数初始化的缩写模式简化了对象方法的声明语法;属性重复声明在ES6严格和非严格模式下都不会报错。

Object.assign()函数可以进行对象多重属性的克隆,统一mixin模式的操作流程。

Object.setPrototypeOf()函数可以更改对象的原型。ES6规范了__proto__ 属性,作为一个存储器属性,它的get方法为Object.getPrototypeOf(),set方法为Object.setPrototypeOf()

super引用永远指向当前对象的原型。super可以作为函数使用,比如super(),也可以作为指针使用,比如super.getGreeting()。不论哪种形式,super调用的内部this永远指向当前的作用域。

转载于:https://www.cnblogs.com/ihardcoder/articles/5293390.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/288598.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php邮件发送tp,Thinkphp5 邮件发送Thinkphp发送邮件

在项目的开发中 用户修改密码,需要发送验证码到用户邮箱,在common.php公共文件中加入以下代码:/*** 系统邮件发送函数* param string $tomail 接收邮件者邮箱* param string $name 接收邮件者名称* param string $subject 邮件主题* param st…

数据库面试题目经典大全

1、事务 事务是指一个工作单元,它包含了一组数据操作命令,并且所有的命令作为一个整体一起向系统提交或撤消请求操作,即这组命令要么都执行,要么都不执行。例如,网上购物的交易过程至少包括以下几个步骤的操作&…

物联网离风口还差最关键一环?

物联网智能时代所带来的不仅仅是物物相联的机会,更是会彻底改变用户和企业之间的关系。用户和硬件、用户和服务之间会更紧密联结在一起。众多业内人士纷纷表示,万物互联的前景虽然美好,但缺乏标准已经成为阻碍产业发展的最大瓶颈。 现阶段痛点…

python创建不可变集合_python不可变集合是什么

在很多的其他语言中在传递参数的时候允许程序员选择值传递还是引用传递(比如c语言加上*号传递指针就是引用传递,而直接传递变量名就是值传递),而python只允许使用引用传递,但是它加上了可变类型和不可变类型,让我们感觉有点混乱了…

前端大屏模板分享-可在线浏览

1. 前言站长以前介绍过这个开源项目,最近又有人在问,索性挂在Dotnet9网站上,方便大家在线浏览,先声明,模板来自下面的仓库:仓库名:大屏数据展示模板作者:lvyeyou开源协议&#xff1a…

linux shell之awk

1 awk awk 是一款设计用于数据流的工具, awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处 awk 脚本的结构基本如下所示: awk BEGIN{print "start" } pattern { commands } END{ print "end" } file awk 脚本通常由3部分组成…

微软的最高市值是多少?

有人说微软1999 年 12 一个月股价达到历史新高 $58.38不准确。我1999年12月22可能会增加微软。公司按照1999年12月27最新价格格(119.125,股票分割后成为59.5625)他给了我一个选项。价格格,微软股价史无前例的成为了最高点&#xf…

使用Scala实现Java项目的单词计数:串行及Actor版本

其实我想找一门“具有Python的简洁写法和融合Java平台的优势, 同时又足够有挑战性和灵活性”的编程语言。 Scala 就是一个不错的选择。 Scala 有很多语言特性, 建议先掌握基础常用的: 变量、控制结构 、正则与模式匹配、集合、文件读写/目录遍…

vnc oracle solaris,在Solaris下安装VNC 远程安装Oracle

最近在折腾oracle ebs的安装 ,用xmanager连接solairs10的过程中,总是连接3个小时左右就挂掉,很郁闷!本文参考eygle的文章1;首先从 eygle提供的网址上下载vnc软件下载得到的文件如下 vnc-3.3.4-solaris_2.5.tar.tar然…

sql CHECK ,UNIQUE 约束(mysql)

check 用来限定值的范围,如下表: CREATE TABLE test22 ( age INT(10), sex VARCHAR(10), name11 VARCHAR(10) NOT NULL, CHECK (age>0) ) 在此,check限制了age的值为0以上 如果想让age的的不唯一呢?那就使用UNIQUE了&…

html+css常用小笔记(持续更新)

1去掉input点击时的蓝色边框outline:none; 2禁止文本选中-webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Chrome/Safari/Opera */ -khtml-user-select: none; /* Konqueror */ -moz-user-select: none; /* Firefox */ -ms-user-select: none; /…

音视频基本概念和FFmpeg的简单入门

写在前面最近正好有音视频编辑的需求,虽然之前粗略的了解过FFmpeg不过肯定是不够用的,借此重新学习下;基本概念容器/文件(Conainer/File):即特定格式的多媒体文件,一般来说一个视频文件是由视频,音频&#…

linux shell之paste合并文件和找到匹配的文件里面替换内容(find和-exec或xargs组合)

1 问题 1)合并2个文件,这里用paste命令 2)找到匹配的文件里面替换内容,这里用find 和 -exec或xargs命令组合 2 实现 1)合并2个文件,这里用paste命令,我们在paste后面加参数-d 然后加" &…

Windows Live Writer 的昨日荣光

今天这一篇文章,想写一写Windows Live Writer这款博客编辑器(最早的一个版本是2007年发布的)。毫不夸张地说,这是为数不多的几款所见即所得的编辑器之一,当然,它的运行速度慢也是一个众所周知的问题。作为一…

qpython执行手机脚本精灵使用教程_Android上执行python脚本-QPython

看书,发现android可以跑python。尝试了一下。首先需要在手机上安装python环境,通过安装apk实现,这个apk叫QPython,还有同类的比如SL4A。QPython的官网:https://www.qpython.com/可以在官网上下载QPython的安装包&#…

猎豹MFC--CMenu菜单 设置主菜单 给主对话框设置菜单 设置快捷菜单

设置主菜单(不是快捷菜单):给主对话框设置菜单:效果如下:修改菜单的ID使之便于记忆:给菜单添加消息处理:添加处理代码:设置快捷菜单:打开对话框,属性添加消息…

SQL主键简单表述

主键(PRIMARY KEY 约束):PRIMARY KEY 约束唯一标识数据库表中的每条记录。 主键就是唯一的,其是索引的一种,并且是唯一性索引的一种。 其实主键就像我们的身份证一样,每一个主键的id就表示着一个特定的一…

oracle 删除空间不足,oracle表空间扩容、创建、删除(解决表空间不足问题)

前言整理一下之前使用oracle数据库遇到的表空间不足的问题,顺便水个博客。oracle表空间操作语句1.改变已存在的数据文件的大小ALTER TABLESPACE app_data ADD DATAFILE ‘D:\ORACLE\PRODUCT\10.2.0\ORADATA\EDWTEST\APP03.DBF‘ SIZE 20480M;2.允许已存在的数据文件…

重复数据删除:块级技术VS.字节级技术

重复数据删除技术能够识别重复的数据,消除冗余,减少需转移或存储的数据的总体容量。在本文中,我将分别对这两种技术加以评论。与块级技术相比,字节级删除技术对数据的检查更加细微,精度更高,但同时需要更加…

Mac升级到Yosemite后默认的php版本不支持imagetfftext函数问题解决

Mac升级到yosemite后,php也自动升级,运行项目的时候发现后台验证码显示不出来。调试一下发现imagetfftext这个函数不存在,应该gd没有安装完全,因为Mac上的php实现系统自带的,只能通过重新安装php来解决【不能通过安装扩…