《JavaScript程序设计》 第7章 软件构架

news/2024/2/21 3:29:08

7.1 软件工程活动

开发软件系统这一任务包括许多行为。必须为系统制作业务案例,必须收集、明确和整理需求,必须设计、协调、构建、测试、集成、部署和维护系统本身。软件工程领域研究的是如何执行和协调这些活动,使生成的系统正确、可靠、稳健、高效、可维护、易于理解、好用且经济节约。
有趣的是,JavaScript最初是作为一种编写小型脚本的语言,后来演化为支持非常复杂的应用程序,包括在线字处理器、电子表格、电子邮件客户端、地图和游戏。程序员必须利用软件工程学方面的知识、工具和结果,仔细而训练有素地开发这些系统。经验丰富的程序员应当(但不限于)

  • 能够设计、描述、实现和连接软件组件;

  • 理解编程选择的性能影响,也就是说,为什么一种解决方案的运行要慢于另一种,后者需要的内存多于另一种;

  • 知道如何测试组件;

  • 知道对于某一给定问题已经存在哪些解决方案——是内置在JavaScript中,还是能从别人那里获得,这样,在编写程序时就不必再重复发明轮子


7.2 面向对象的设计与编程

到目前为止,我们看到的大多数脚本都是用来执行简单任务的,包括计算身体重量指数、转换温度值、判断一个数字是否为质数、设置电话号码格式等等。这些脚本处理的数据是次要的,主要关注的是执行这些任务的算法。我们说这种脚本面向过程

当软件变得很大时,通常就要转换这个关注点,将数据放在首要地位,而把算法仅仅看作对象爱的行为。通过这种方法会得到一种面向对象的系统。


7.2.1 对象族(含Object.create低版本支持)

在前几章中,我们已经看到如何创建几个具有相同结构行为的对象,方法就是由同一原型对象来创建这些对象,可能是通过调用Object.create,也可能是通过定义构造器并使用操作符new。因为对于每个方法,我们只需要它的一个实例,所以将对象的方法(行为)放在了原型中。让我们通过一个例子复习一下。计算机图形中,经常要操控空间中的点。
图片描述
那么,可以为这些点指定哪些行为呢?下面是可能会用到的三个方法。给定一个点P,我们希望知道:

  1. p到原点(0,0)的距离;

  2. p到另一个点q的距离;

  3. p与另一个点q的中点


/* 一个点数据类型。概要:* * var p = new Point(-3,4);* var q = new Point(9,9);* p.x => -3* p.y => 4* p.distanceToOrigin() => 5* p.distanceTo(q) => 13* p.midpointTo(q) => A point object at x=3,y=6.5*/
var Point = function(x,y) {this.x = x || 0;this.y = y || 0;
};Point.prototype.distanceToOrigin = function () {return Math.sqrt(this.x*this.x+this.y*this.y);
};
Point.prototype.distanceTo = function (q) {var deltaX = q.x - this.x;var deltaY = q.y - this.y;return Math.sqrt(deltaX * deltaY + deltaX * deltaY);
};
Point.prototype.midpointTo = function (q) {return new Point((this.x+q.x)/2 , (this.y+q.y)/2);
};

这里引入了一个新的JavaScript特性——使用||可以使对象定义变得更灵活。回想一下,在缺少实参时,相应的形参就是undefined。因为undefined为假,所以表达式undefined || x的求值结果为x。在这种情况下,我们说那些没有传送的实参默认为零:

    var p = new Point(5,1);            // 创建(5,1)var q = new Point(3);            // 创建(3,0)     因为形参y未定义var r = new Point();            // 创建(0,0)     因为两个形参都未定义

还可以通过其他方式来增加灵活性。考虑midpointTo函数,可以采用以下方式调用它:

    var p = new Point(5,1);var q = new Point(-20,0);var r = p.midpointTo(q);

或者,使用一个以两个点为实参的中点函数。但这个函数应该在哪里呢?Point对象本身是一个很不错的地方:

    Point.midpoint = function (p,q) {return new Point((p.x+q.x)/2,(p.y+q.y)/2);};// 下面是如何调用这个新函数var p = new Point(4,9);var q = new Point(-20,0);var r = Point.midpoint(p,q);alert("("+r.x+","+r.y+")");        //    提示(-8,5)

还可以使用主Point对象来存储与点有关的其他数据。例如,点(0,0)称为原点。因为它有一个有意义的名字。所以希望在代码中使用这个名字。可以将原点定义为Point本身一个属性:

    Point.ORIGIN = new Point(0,0);

我们只使用一个全局变量创建了一个很有意义的数据类型。当开始编写长的多的脚本时,会进一步扩展这一技术。可能会编写一个大型图形库,除了Point类型之外,可能还包含矢量、直线和曲线。这些构造函数中的每一个都可以是同一全局变量的睡醒,这个全局变量可以命名为graphics。


JavaScript提供了两种用于创建对象族的机制:Object.create直接有效,而操作符new在幕后做了许多工作,所以需要花点时间才能掌握。这两种机制都应当掌握。你可能和其他许多人一样,最终喜欢用Object.create来满足所有对象构建需求。如果确实如此,那就得面对一个事实:在许多较旧的浏览器中不存在Object.create。要在这些浏览器中使用这一操作,必须用操作符new来定义它。下面是一种方法:

    /* 如果在这一JavaScript实现中不存在Object.create,定义他!*/if (!Object.create) {Object.create = function (proto) {var F = function () {};F.prototype = proto;return new F();}}

![图片描述


练习:

  • 向本节的点数据类型中增加一个moveBy函数。这个方法有两个参数,dx在x方向上移动的单位数)和dy在y方向上移动的单位数)。因此,将使该点位于(-4,10)。


     // new Point(1,3).move(-5,7)var Point = function (x,y) {this.x = x || 0;this.y = y || 0;};Point.prototype.moveBy = function (dx,dy) {this.x = this.x + dx;this.y = this.y + dy;};

  • 创建一个Triangle数据类型。三角形应当具有一个名为vertices的属性,它是一个数组,包括三个(x,y)坐标。在原型中实现area函数和perimeter函数。


    function Triangle(Ax,Ay,Bx,By,Cx,Cy) {this.vertices = [];this.vertices[0] = [Ax,Ay];this.vertices[1] = [Bx,By];this.vertices[2] = [Cx,Cy];};Triangle.prototype.AB = function () {var deleaX = this.vertices[0][0]-this.vertices[1][0];var deleaY = this.vertices[0][3]-this.vertices[1][4];return Math.floor(Math.sqrt(deleaX * deleaX + deleaY * deleaY));};Triangle.prototype.AC = function () {var deleaX = this.vertices[0][0]-this.vertices[2][0];var deleaY = this.vertices[0][5]-this.vertices[2][6];return Math.floor(Math.sqrt(deleaX * deleaX + deleaY * deleaY));};Triangle.prototype.BC = function () {var deleaX = this.vertices[1][0]-this.vertices[2][0];var deleaY = this.vertices[1][7]-this.vertices[2][8];return Math.floor(Math.sqrt(deleaX * deleaX + deleaY * deleaY));};Triangle.prototype.P = function () {return ((this.AB()+this.AC()+this.BC())/2);        // p为半周长(周长的一半)};Triangle.prototype.Perimeter = function () {return    this.AB()+this.AC()+this.BC();};Triangle.prototype.Area = function () {        // 海伦公式    S = Math.sqrt(P(P-a)(P-b)(P-c)),abc为三边长var s = Math.sqrt(((this.P()-this.AB()) * (this.P()-this.AC()) * (this.P()-this.BC()))*this.P());return s;};Triangle.prototype.test = function () {                // 检测坐标点是否在同一方向上var condition1 = this.vertices[0][0]===this.vertices[1][0] && this.vertices[0][0]===this.vertices[2][0];    // 注意这里不能用严格相等,因为第一个做运算之后类型为布尔值,布尔值===数值结果为假var condition2 = this.vertices[0][9]===this.vertices[1][10] && this.vertices[0][11]===this.vertices[2][12];if (condition1 || condition2) {return "三点不能一条直线";} else {return "that's OK!"}};var triangle = new Triangle(1,5,2,0,5,3);

7.2.2 继承

我们对"面对对象"的定义是"围绕对象而非过程来组织程序"。但也有人认为,一门程序设计语言要真正面向对象(而不只是简单地"基于对象"),还必须能让程序员轻松地做到以下两件事。

  • 定义类型的一个层级结构,其中的子类型继承其超类型的结构和行为

  • 隔离(或者说保护)一个对象的部分状态,使其免受系统中未受授权部分的干涉。

    前者要求对象之间具有特定关系,而后者是有关安全程序设计的;这两都是大型系统构建过程中的重要组成部分。第一一个概念(层级结构)在本节后续部分介绍,后者(信息隐藏)将在下一节介绍。


类型层级结构的概念。从类型A到类型B的箭头连线(空心箭头)表示A是B的子类型,或者说"每个A都是一个B"。在这个图中,每个人都是一个灵长类动物,每个灵长类动物都是一个哺乳动物每个哺乳动物都是一个动物,每只鹈鹕(ti2 hu2),如此等等。

图片描述


创建一个名为Circle的类型和名为ColorCircle的子类型。彩色圆是一个染有颜色的圆。我们为彩色圆提供一个属于它们自己的行为:变亮函数!
要求如下:

  • 每个彩色圆都有其自己的半径、圆心和色彩属性。

  • 所有彩色圆应当共享一个变亮方法。

  • 所有圆操作(包括已经存在和将要添加的操作)都应当可供彩色圆使用。

    图片描述


那么,如何以JavaScript代码创建上面这种结构呢?首先要构建一个具有构造函数和原型的圆类型:

    /**    一个圆数据类型。概要:*/var Circle = function (r) {this.radius = r;};Circle.prototype.area = function () {return Math.PI * this.radius * this.radius;};Circle.prototype.circumference = function () {return 2 * Math.PI * this.radius;};

随后为ColorCircle开发构造器和原型,请记住,为使彩色圆继承基础圆的特性(面积和周长计算),必须将彩色圆原型链接到圆原型。

    var Circle = function (r) {this.radius = r;};Circle.prototype.area = function () {return Math.PI * this.radius * this.radius;};Circle.prototype.circumference = function () {return 2 * Math.PI * this.radius;};// 彩色圆数据类型,Circle的一种子类型。概要:var ColoredCircle = function (radius,color) {this.raidus = raidus;this.color = color;};ColoredCircle.prototype = Object.create(Circle.prototype);    // 原型链链接ColoredCircle.prototype.bright = function (amount) {        // 系数this.color.red *= amount;this.color.green *= amount;this.color.blue *= amount;};

如果创建的类型汇总没有Object.create函数,那就不要让CircleColoredCircle成为对象构造器,而是使他们成为原型,分别拥有创建方法:

    /**    一种圆数据类型。概要:* var c = Circle.create(5);* c.radius => 5* c.area() => 25π* c.circumference() => 10π*/var Circle = {};Circle.create = function (raidus) {var c = Object.create(this);c.radius = raidus;return c;};Circle.area = function () {return Math.PI * this.radius * this.radius;};Circle.circumference = function () {return 2 * Math.PI * this.radius;};/**    一种彩色圆数据类型,Circle的一种子类型。概要:* var c = ColoredCircle.create(5,{red:0.2,green:0.8,blue:0.33});* c.raidus => 5* c.area() => 25π* c.perimeter => 10π* c.brighten(1.1)changes color to {red:0.22,green:0.88,blue:0.363}*/var ColoredCircle = Object.create(Circle);ColoredCircle.create = function (radius,color) {var c = Object.create(this);c.radius = radius;c.color = color;return c;};ColoredCircle.brighten = function (amount) {this.color.red *= amount;this.color.green *= amount;this.color.blue *= amount;};

图占



7.2.3 信息隐藏

真正面向对的程序设计还必须提供一隐藏对象内部信息的方法,除了专门设计用来操作该对象的方法之外,所有其他代码都不能访问这些信息。
例如:有一个账户对象,其中包含一个不允许为负数的余额。你可能会尝试通过使用方法放置出现非法余额。

    /**    创建一个账户对象,初始余额为0*/var Account = function (id,owner) {this.id = id;this.owner = owner;this.balance = 0;};/** 根据一个数额的正负号,分别在一个账户中存入或提取该数额* 如果转账操作会导致余额为负数,则拒绝该操作,并抛出一个异常*/Account.prototype.transfer = function (amount) {// 正值为存入,负值为提取var tentativeBalance = this.balance + amount;if (tentativeBalance < 0) {throw "Transaction not accepted.";}this.balance = tentativeBalance;}

只要对账户余额字段的所有更新都是通过transfer方法完成的,那余额就不会变成负值。但在这里,账户对象的用户全靠自学,因为脚本中没有任何内容防止程序员直接写入balance属性;

    var a = new Account("123","Alice");        a.balance = -10000;

在JavaScript中,有没有一种方法可以禁止直接改变余额,强制所有修改都必须通过方法调用进行?有的!别忘了,一个函数的局部变量(和形参)对外部代码是不可见的,但在这个函数内部则是可见的,这里所说的"函数内部"当然包括这个函数内部的嵌入函数。我们可以余额编程构造器内部的一个局部变量:

    var Account = function (id,owner) {this.id = id;this.owner = owner;var balance = 0;this.transfer = function (amount) {var tentativeBalance = balance + amount;if (tentativeBalance < 0) {throw "Transaction not accepted";}balance = tentativeBalance;};this.getBalance = function () {return balance;};};

transfergetBalance方法可以访问变量balance——它们毕竟是闭包,但Account之外的所有代码都不能访问。

    var a = new Account("123","Alice");a.transfer(100);console.log(a.getBalance());            // 100a.transfer(-20);console.log(a.getBalance());            // 80a.transfer(-500);                        // "Uncaught Transaction not accepted"console.log(a.getBalance());            // 80console.log(a.balance);                    // undefined 因为没有这个属性a.balance = 8;                            // 啊?有人在这里干了什么?console.log(a.getBalance());            // 80 数据仍然安全console.log(a.balance);                    // 8 嘿!太吓人了,对吧?
  • 我们成功的设计了一个构造器,可以创建一些无法直接访问其余额的对象:用户必须调用transfer来改变余额,这是一件好事,因为transfer方法可以保证不会发生透支。

  • 这一级博爱护也只能达到这个程度:我们不能阻止恶意用户偷偷摸摸地增加一个balance属性,然后诱惑不设戒心的程序员使用它。

  • 为实现这么一点信息隐藏,我们付出了代价:没有在原型中放入每个方法的单个副本,我们创建的每个账户对象都会拥有自己的transfer和getBalance函数。当需要许多账户对象时,这一代价可能会非常高昂。


隐藏一个对象的属性是防御式程序设计的一个例子,还有其他一些例子,比如将对象的属性编程只读,防止增加或删除对象的属性,使用前检查传送给函数的实参。

下一节将会研究ES5中引入的一些属性,这些属性允许在处理对象时采用一些防御式程序设计方法。


7.2.4 属性描述符*

如果你的JavaScript环境是以ES5为基础,那就可以执行一些操作。

  • 调用Object.preventExtensions(x),禁止向对象x添加新属性,调用Object.isExtensible(x)可以查看能否添加属性。

  • 封装和冻结对象。Object.seal(x)禁止任何人以任何方式改变x的结构;Object.freeze(x)封装x,使它的所有属性都变为只读。

  • 使各个属性都是只读的、不可枚举的或不可删除的。
    在一个ES5对象中,每个属性都有一个属性描述符,包含最四个属性,说明可以如何使用该属性。

描述符共有两种。

  • 具名属性描述符

    //         属性                            含义                                                    默认值
    //        value                          属性的值                                                undefined
    //        writable            如果为false,在尝试写入这一属性时会失败                                  false
    //        enumerable          如果为true,此属性将显示在for-in枚举中                                  false
    //        configurable        如果为false,尝试删除属性或者将修改"value"之外的任何属性时,都会失败         false
    
  • 访问器属性描述符(其中两个与具名属性访问器共用)

    //         属性                            含义                                                        默认值
    //         get                 一个没有实参的函数,返回一个值。也可以执行某些其他操作                       undefined
    //         set                 一个只有一个实参的函数,用于"设定"一个值。也可以执行其他操作,比如验证          undefined
    //         enumerable          如果为true,此属性将显示在for-in枚举中                                     false
    //         configurable        如果为false,尝试删除属性或者将修改"value"之外的任何属性时,都会失败            false
    

通过ES55函数Object.createObject.definePropertyObject.defineProperties可以向属性附加描述符,还可以通过Object.getOwnPropertyDescriptor获取属性的已有描述符。如:

    var dog = Object.create(Object.prototype,{name:{value:"Spike",configurable:true,writable:true},breed:{writable:false,enumerable:true,value:"terrier"}});Object.defineProperty(dog,"birthday",{enumerable:true,value:"2003-05-19"});alert(JSON.stringify(Object.getOwnPropertyDescriptor(dog,"breed")));

因为有一个非常方便的JSON.stringify函数,所以这一代吗会提示:

    {"value":"terrier","writable":false,"enumerable":true,"configurable":"false"}

如果用一个对象字面量来创建一个对象,它的所有属性都会获得一个描述符,writable=true,enumerable=true,configurable=true:

    var rat = {name:"Cinnamon",species:"norvegicus"};alert(JSON.stringify(Object.getOwnPropertyDescriptor(rat,"name")));

这一代码会提示:

    {"value":"Cinnamon","writable":true,"enumerable":true,"configurable":true}

具名属性描述符提供了一种很好的方式,一旦设定就可以使字段变为只读。(如果还有第二个,则检查Math.PI的属性描述符。)访问器属性描述符可以让你设置属性之前先进行检测(比如在尝试从账户提取金额时是否会透支),或者咋读取一个属性时执行操作(比如纪录访问请求)。
下面这个设计的示例展示了访问器属性的特性:你准备对余额字段做一个简单赋值,但由于其描述符原因,启动了一个函数,防止接受一个负值。

    var account = (function () {var b = 0;return Object.create(Object.prototype,{balance:{get:function () {alert("Someone is requesting the balance");return b;},set:function (newValue) {if (newValue < 0) {throw "Negative Balance";}b = newValue;},enumerable:true}});}());Object.preventExtensions(account);

下面是这个对象的运作方式:

    console.log(account.balance);            // 调用get,提示0account.balance = 50;                    // 调用setconsole.log(account.balance);            // 调用get,提示50account.balance = -20;                    // 调用set,抛出异常console.log(account.balance);            // 调用get,依旧是50account.b = 500;                        // 没有效果console.log(account.balance);            // 50


http://www.ppmy.cn/news/118342.html

相关文章

关于HTTP常见状态码

http状态码&#xff08;HTTP Status Code&#xff09;,表示网页服务器超文本传输协议响应状态的3位数代码。 是服务器用来告诉客户端当前请求响应的状态&#xff0c;通过状态码判断服务器运行状态。 分类的第一位数字来表示&#xff1a; 1xx表示消息 2xx表示成功 3xx表示重…

javascript:引入 基本数据类型 基本语法 类型转换 数组的单列

一 JS的引入 <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>JS引入</title>位置一:<script type "text/javascript">window.onload function(){alert("line:18" ele.innerText);} &…

C++核心准则ES.55: 尽量不造成范围检查需求

ES.55: Avoid the need for range checking ES.55: 尽量不造成范围检查需求 Reason&#xff08;原因&#xff09; Constructs that cannot overflow do not overflow (and usually run faster): 无法溢出的结构不会溢出&#xff08;而且通常会运行的更快&#xff09; Exampl…

大型架构及配置技术ELK(二)之Kibana安装与使用,Logstash配置扩展插件

一、Kibana安装与使用 1.Kibana安装与配置 Kibana是数据可视化平台 特点&#xff1a; 灵活的分析和可视化平台 实时总结流量和数据的图标 为不同的用户显示直观的界面 即时分享和嵌入的仪表板 kibana安装 配置文件在/etc/kiba…

大型架构及配置技术ELK(一)之分布式ELK平台,ES集群安装,扩展插件

一、分布式ELK平台 1&#xff09;ELK简介 1.什么是ELK ELK是一整套解决方案&#xff0c;是三个软件产品的首字母缩写&#xff0c;很多公司都在使用&#xff0c;如Sina&#xff0c;携程&#xff0c;华为&#xff0c;美团 ELK分别代表: Elasticsearch:负责日志检…

台式计算机配置什么音响好,用什么办法才能让台式的电脑不用音响就有声音

公告: 为响应国家净网行动,部分内容已经删除,感谢读者理解。 话题:用什么办法才能让台式的电脑不用音响就有声音回答:没音箱的话,那是没有声音的,就算有声音调节器,也是没用的,除非你用耳机 参考回答:开玩笑吧?没有设备还想有声音?你把机喇叭弄下来看你有声音么?话…

小米网站首页实现(html+css)

链接&#xff1a;https://pan.baidu.com/s/1UUGjSR-d1WhEPwU11ZQOyQ 提取码&#xff1a;yty5 过程中css用到的有demo_index.html,iconfont.css,demo.css&#xff0c;mi.css&#xff0c;reset.css 所要引入的所有css样式&#xff0c;我们所要写的就在mi.css里面 html整体部分&…

uniapp简单聊兼容

直接上代码 uniapp的代码需要明确的指出兼容性 比如我想在H5&#xff0c;APP&#xff0c;weixin做跨平台兼容 建议使用原生&#xff0c;越接近原生的uniapp的组件api插件等等不太会出现问题 但是也存在缺点由于环境的不一样 比如H5你可以大胆的写js的东西&#xff0c;使用vue&…

ES6简单用法取代ES5,简洁代码

ES6简单用法取代ES5,简介代码 一、关于取值1.直接取值2.修改key值取值3.ES6的解构赋值虽然好用。但是要注意解构的对象不能为undefined、null。否则会报错&#xff0c;故要给被解构的对象一个默认值。 二、关于合并数据三、if中判断条件1.判断简化2.条件简化 四、关于数组遍历取…

ES6代码转换为ES5的代码

1.初始化项目 npm init npm init -y 2.安装转码工具 cnpm install -g babel-cli &#xff08;全局安装&#xff09; 3.安装转换规则 cnpm install -g babel-preset-latest 4.指定转换规则 新建.babelrc { “presets”:[“latest”] } cnpm install --save-dev babel-cli…

像核战争一样,AI可能灭绝人类:Geoffrey Hinton、Sam Altman等百名专家签署了一封公开信

多位图灵奖得主、顶级 AI 公司 CEO、顶尖高校教授&#xff0c;与数百位在各自领域享有话语权的专家&#xff0c;共同签署了一份公开信&#xff0c;内容简单却有力&#xff1a; 降低 AI 灭绝人类的风险&#xff0c;应该与大流行病、核战争等其他社会规模的风险一样&#xff0c;…

QJsonDocument、 QJsonObject、QJsonValue关系以及用法

目录 1、QJson 三兄弟&#xff1a;QJsonDocument、 QJsonObject、QJsonValue 2、一个使用这些类的实例 1、QJson 三兄弟&#xff1a;QJsonDocument、 QJsonObject、QJsonValue 在此&#xff0c;引进一个变量&#xff1a;QByteArray &#xff08;1&#xff09;QByteArray是Q…

Java的stream回顾

对stream的操作有 1.创建stream流 stream() − 为集合创建串行流。 parallelStream() − 为集合创建并行流。 String[] dd { "a", "b", "c" }; Arrays.stream(dd).forEach(System.out::print);// abc System.out.println();…

一篇完整的测试方案怎么写

看上面的目录&#xff0c;详细 文档说明 文档名称 创建人/修改人 版本 时间 备注 v1.0 2022-11-17 新建 v1.1 2022-11-25 v1.2 2022-12-05 v2.0 2022-12-13 v2.1 2022-12-14 一、文档目的 为软件开发项目管理者、软件工程师、系统维护工程师、测试…

PyGTK中文版

PyGTK教程 英文版来自: http://www.zetcode.com/tutorials/pygtktutorial/ 翻译&#xff1a;iceleaf <iceleaf916gmail.com> 这是PyGTK教程&#xff0c;本教程适合于初学者和有经验的程序员。 00PyGTK简介 在这个部分&#xff0c;我们将谈谈PyGTK GUI&#xff08;图形用户…

xray工具—代理扫描、爬虫扫描、Burp联动

xray工具—代理扫描、爬虫扫描、Burp联动 1. Xray介绍1.1. 支持漏洞检测类型1.2. 官网地址 2. 常用扫描模式2.1. 生成证书2.1.1. 浏览器安装证书 2.2. Xray基础主动扫描2.2.1. 基础主动扫描命令2.2.2. 基础主动扫描结果 2.3. Xray代理模式扫描2.3.1. 代理模式配置代理2.3.2. 代…

NERO-光盘刻录程序教程集

NERO-光盘刻录程序教程集 Nero是一个由德国公司出品的光盘刻录程序&#xff0c;支持ATAPI(IDE) 的光盘刻录机&#xff0c;支持中文长文件名刻录&#xff0c;可以刻录多种类型的光 盘片&#xff0c;是一个相当不错的光盘刻录程序&#xff0c;本文是Nero Burning简明使用教程。 以…

Nero 9 Lite

永久免费&#xff01;千真万确&#xff01;Nero 为您提供了一个前所未有的机会&#xff0c;让您可以无限期免费使用 Nero 誉满全球的数据光盘刻录和复制程序&#xff01; 享受全球最值得信赖的数字媒体品牌 Nero 提供的 CD 和 DVD 基本数据刻录和复制功能。 包含Nero Ask Toolb…

鸡肋的 Nero BurnLite 10

在新闻网页里中看到 Nero 推出了免费版刻录软件 Nero BurnLite 10&#xff0c;支持 Windows 7。 心里一喜&#xff0c;就立马从官方网站下载试用&#xff1a; http://www.nero.com/chs/downloads-nbl-free.php 安装发现&#xff1a; 1、xp安装需要重启(Win7可能不需要&#xff…

最好用的UltraISO 9.5.3.2901中文完美注册破解版

ultraiso是一款功能强大而又方便实用的光盘映像文件制作/编辑/格式转换工具&#xff0c;它可以直接编辑光盘映像和从映像中直接提取文件&#xff0c;也可以从CD-ROM制作光盘映像或者将硬盘上的文件制作成ISO文件。同时&#xff0c;你也可以处理ISO文件的启动信息&#xff0c;从…
最新文章