金沙棋牌官方平台

当前位置:金沙棋牌 > 金沙棋牌官方平台 > 理解JavaScript的原型属性,的原型属性

理解JavaScript的原型属性,的原型属性

来源:http://www.logblo.com 作者:金沙棋牌 时间:2019-10-09 20:27

prototype 属性名称带来的误解

有一些关于 JavaScript 的原型的误解。 一个对象的原型与对象的 prototype 属性并非一回事。 前者用于在原型链中匹配不存在的属性。后者用于通过 new 关键字创建对象,它将作为新创建对象的原型。 理解二者的差异,将帮助你彻底理解 JavaScript 中的原型特性。

在我们的例子中, Rectangle.prototype 是用 new Rectangle() 创建出来对象的原型, 而 Rectangle 的原型实际上是 JavaScript 的 Function.prototype。(子对象的原型是父对象的 prototype 属性)

对象中保存原型的变量,也被称之为内部原型引用(the internal prototype link),历史上也曾称之为 __proto__ ,对这个称谓始终存在一些争议。 更精确的,它可以被称为 Object.getPrototypeOf(...) 的返回值。

2 赞 5 收藏 2 评论

2. JavaScript 实现继承的语言特性

  • 当尝试访问 JavaScript 对象中不存在的属性时,解析器会查找匹配的对象原型。例如调用 car.toString(),如果 car 没有 toString 方法,就会调用 car 对象的原型。 这个查找过程会一直递归, 直到查找到匹配的原型或者继承链尽头。

  • 调用  new Car() 会创建一个新的对象,并初始化为 Car.prototype。 这样就允许为新对象设置原型链。需要注意的是,new Car() 只有当  Car 是函数时才有意义。 此类函数即所谓构造函数

  • 调用对象的一个成员函数时, this 的值被绑定为当前对象。例如调用 "abc".toString(),this 的值被设置为 "abc",然后调用 toString 函数。该技术支持代码重用:同样的代码,可在 this 为各种不同的值时调用。对象的成员函数,也被称为对象的方法。

   金沙棋牌官方平台 1

  首先,我们定义构造函数 Rectangle。 按照规范,我们大写构造函数名首字母,表明它可以用 new 调用,以示与其他常规函数的区别。构造函数自动将 this 赋值为一空对象,然后代码中用 x 和 y 属性填充它,以备后用。然后, Rectangle.prototype 新增一个通过 x 和 y 属性计算周长成员函数。 注意 this 的使用,在不同的对象中,this 会有不同的值,这些代码都可以正常工作。最后, 一个名为 rect 的对象创建出来了。 它继承了 Rectangle.prototype, 我们可以调用 rect.perimeter(), 然后将结果打印到控制台。

最近语言学习有些疯狂, 从Ruby到Lisp, 然后是C#, 既然已经疯狂了, 就顺面学习一下javascript吧. 对javascript的印象一直不佳, 从骂脏话最多的使用者, 到使用者平反的世界上最被误解的语言, 从所谓的让人抓狂的特性, 到世界上任何可以用javascript实现的东西, 最终都会被javascript实现, 并且, 这是最后一个实现. 出处太多, 不一一列举, 知者已知, 不知者也没有必要为了这些无聊的言论特意找出处了.

六、时间对象

在JavaScript中,Date对象用来表示日期和时间。

要获取系统当前时间,用:

var now = new Date();
now; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
now.getFullYear(); // 2015, 年份
now.getMonth(); // 5, 月份,注意月份范围是0~11,5表示六月
now.getDate(); // 24, 表示24号
now.getDay(); // 3, 表示星期三
now.getHours(); // 19, 24小时制
now.getMinutes(); // 49, 分钟
now.getSeconds(); // 22, 秒
now.getMilliseconds(); // 875, 毫秒数
now.getTime(); // 1435146562875, 以number形式表示的时间戳

注意,当前时间是浏览器从本机操作系统获取的时间,所以不一定准确,因为用户可以把当前时间设定为任何值。

如果要创建一个指定日期和时间的Date对象,可以用:

var d = new Date(2015, 5, 19, 20, 15, 30, 123);
d; // Fri Jun 19 2015 20:15:30 GMT+0800 (CST)

JavaScript的月份范围用整数表示是0~11,0表示一月,1表示二月

第二种创建一个指定日期和时间的方法是解析一个符合ISO 8601格式的字符串:

var d = Date.parse('2015-06-24T19:49:22.875+08:00');
d; // 1435146562875

但它返回的不是Date对象,而是一个时间戳。不过有时间戳就可以很容易地把它转换为一个Date:

var d = new Date(1435146562875);
d; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)

时区

也可以显示调整后的UTC时间:

var d = new Date(1435146562875);
d.toLocaleString(); // '2015/6/24 下午7:49:22',本地时间(北京时区+8:00),显示的字符串与操作系统设定的格式有关
d.toUTCString(); // 'Wed, 24 Jun 2015 11:49:22 GMT',UTC时间,与本地时间相差8小时

那么在JavaScript中如何进行时区转换呢?实际上,只要我们传递的是一个number类型的时间戳,我们就不用关心时区转换。任何浏览器都可以把一个时间戳正确转换为本地时间。
时间戳是个什么东西?时间戳是一个自增的整数,它表示从1970年1月1日零时整的GMT时区开始的那一刻,到现在的毫秒数。假设浏览器所在电脑的时间是准确的,那么世界上无论哪个时区的电脑,它们此刻产生的时间戳数字都是一样的,所以,时间戳可以精确地表示一个时刻,并且与时区无关。所以,我们只需要传递时间戳,或者把时间戳从数据库里读出来,再让JavaScript自动转换为当地时间就可以了。
要获取当前时间戳,可以用:

if (Date.now) {
    alert(Date.now()); // 老版本IE没有now()方法
} else {
    alert(new Date().getTime());
}

举个栗子

我们用面向对象编程,实现一个计算矩形周长的例子。

JavaScript

function Rectangle(x, y) { this.x = x; this.y = y; } Rectangle.prototype.perimeter = function() { return 2 * (this.x + this.y); } var rect = new Rectangle(1, 2); console.log(rect.perimeter()); // outputs '6'

1
2
3
4
5
6
7
8
9
10
11
function Rectangle(x, y) {
    this.x = x;
    this.y = y;
}
 
Rectangle.prototype.perimeter = function() {
    return 2 * (this.x + this.y);
}
 
var rect = new Rectangle(1, 2);
console.log(rect.perimeter()); // outputs '6'

首先,我们定义构造函数 Rectangle。 按照规范,我们大写构造函数名首字母,表明它可以用 new 调用,以示与其他常规函数的区别。构造函数自动将 this 赋值为一空对象,然后代码中用 xy 属性填充它,以备后用。

然后, Rectangle.prototype 新增一个通过 xy 属性计算周长成员函数。 注意 this 的使用,在不同的对象中,this 会有不同的值,这些代码都可以正常工作。

最后, 一个名为 rect 的对象创建出来了。 它继承了 Rectangle.prototype, 我们可以调用 rect.perimeter(), 然后将结果打印到控制台。

1.原型继承

  面向对象编程可以通过很多途径实现。其他的语言,比如 Java,使用基于类的模型实现: 类及对象实例区别对待。但在 JavaScript 中没有类的概念,取而代之的是一切皆对象。JavaScript 中的继承通过原型继承实现:一个对象直接从另一对象继承。对象中包含其继承体系中祖先的引用——对象的 prototype 属性。

 }, 

3. JavaScript的作用域链

由于JavaScript中的每个函数作为一个作用域,如果出现函数嵌套函数,则就会出现作用域链。

xo = 'alex';
function Func(){
    var xo = "seven";
    function inner(){
        var xo = 'alvin';
        console.log(xo);
    }
    inner();
}
Func();

如上述代码则出现三个作用域组成的作用域链,如果出现作用域链后,那么寻找变量时候就会出现顺序,对于上述实例:
当执行console.log(xo)时,其寻找顺序为根据作用域链从内到外的优先级寻找,如果内层没有就逐步向上找,直到没找到抛出异常。

理解JavaScript的原型属性

2016/06/21 · JavaScript · 2 评论 · 原型

本文由 伯乐在线 - alvendarthy 翻译,sunshinebuel 校稿。未经许可,禁止转载!
英文出处:bytearcher。欢迎加入翻译组。

理解 JavaScript 的prototype属性不太容易。你也许知道它同面向对象编程(OOP)和对象继承有关,但未必对其技术原理非常清楚。

prototype 属性名称带来的误解

  有一些关于 JavaScript 的原型的误解。 一个对象的原型与对象的 prototype 属性并非一回事。 前者用于在原型链中匹配不存在的属性。后者用于通过 new 关键字创建对象,它将作为新创建对象的原型。 理解二者的差异,将帮助你彻底理解 JavaScript 中的原型特性。

  Rectangle.prototype 是用 new Rectangle() 创建出来对象的原型, 而 Rectangle 的原型实际上是 JavaScript 的 Function.prototype。(子对象的原型是父对象的 prototype 属性 对象中保存原型的变量,也被称之为内部原型引用(the internal prototype link),历史上也曾称之为 __proto__ ,对这个称谓始终存在一些争议。 更精确的,它可以被称为 Object.getPrototypeOf(...) 的返回值。

因为是hash表, 所以动态添加内容不在话下.

五、js的作用域

原型继承

面向对象编程可以通过很多途径实现。其他的语言,比如 Java,使用基于类的模型实现: 类及对象实例区别对待。但在 JavaScript 中没有类的概念,取而代之的是一切皆对象。JavaScript 中的继承通过原型继承实现:一个对象直接从另一对象继承。对象中包含其继承体系中祖先的引用——对象的 prototype 属性。

class 关键字是在 ES6 中首次引入 JavaScript 的。其实,它并没有为面向对象继承引入新模型, class 关键字通过语法糖,实现了本文介绍的原型特性和构造函数。

javascript只有函数级别的作用域, 函数外都是全局作用域, 没有块级作用域. 意味着类似for, while, if等块中定义的实际是全局变量. 这个设定在现代语言中是逆天的. 于是, 借助匿名函数, 人们想出了更加诡异的解决方案来模拟块级作用域.

四、函数进阶

关于作者:alvendarthy

金沙棋牌官方平台 2

一个热爱生活的家伙! 个人主页 · 我的文章金沙棋牌官方平台, · 16

金沙棋牌官方平台 3

继承

七、面向对象编程

JavaScript的面向对象编程和大多数其他语言如Java、C#的面向对象编程都不太一样。
JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。
JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。

JavaScript 实现继承的语言特性

以下语言特性共同实现了 JavaScript 继承。

  • 当尝试访问 JavaScript 对象中不存在的属性时,解析器会查找匹配的对象原型。例如调用 car.toString(),如果 car 没有 toString 方法,就会调用 car 对象的原型。 这个查找过程会一直递归, 直到查找到匹配的原型或者继承链尽头。
  • 调用  new Car() 会创建一个新的对象,并初始化为 Car.prototype。 这样就允许为新对象设置原型链。需要注意的是,new Car() 只有当  Car 是函数时才有意义。 此类函数即所谓构造函数。
  • 调用对象的一个成员函数时, this 的值被绑定为当前对象。例如调用 "abc".toString()this 的值被设置为 "abc",然后调用 toString 函数。该技术支持代码重用:同样的代码,可在 this 为各种不同的值时调用。对象的成员函数,也被称为对象的方法。

也就是说, 相对一些语言(比如php)会自动的将字符串转为数字来说, javascript是倾向于将数字转为字符串的. 其实因为这种用法过于灵活, 即使是Ruby和Python这样以灵活著称的语言都是不允许这样的自动类型转换的.

4.2 reduce()

Array的reduce()把一个函数作用在这个Array的[x1,x2,x3...]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算,其效果就是:
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
例如对一个Array求和,就可以用reduce实现:

var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
    return x + y;
}); // 25

var rect1 = new Rectangle(2, 4);

4.3 filter()

用于把Array的某些元素过滤掉,然后返回剩下的元素。与python是一样样的。
和map()类似,Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。
例如,在一个Array中,删掉偶数,只保留奇数,可以这么写:

var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {
    return x % 2 !== 0;
});
r; // [1, 5, 9, 15]

// out: 24

2. JavaScript采用函数作用域

在JavaScript中每个函数作为一个作用域,在函数外部无法访问函数内部的变量。

function Main(){
    var innerValue = 'seven';
}
Main();
console.log(innerValue);
// 报错:Uncaught ReferenceError: innerValue is not defined

}

7.3 class继承

JavaScript的对象模型是基于原型实现的,特点是简单,缺点是理解起来比传统的类-实例模型要困难,最大的缺点是继承的实现需要编写大量代码,并且需要正确实现原型链。

有没有更简单的写法?有!

新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。

我们先回顾用函数实现Student的方法:

function Student(name) {
this.name = name;
}

Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
}
如果用新的class关键字来编写Student,可以这样写:

class Student {
constructor(name) {
this.name = name;
}

hello() {
    alert('Hello, ' + this.name + '!');
}

}
比较一下就可以发现,class的定义包含了构造函数constructor和定义在原型对象上的函数hello()(注意没有function关键字),这样就避免了Student.prototype.hello = function () {...}这样分散的代码。

最后,创建一个Student对象代码和前面章节完全一样:

var xiaoming = new Student('小明');
xiaoming.hello();
class继承

用class定义对象的另一个巨大的好处是继承更方便了。想一想我们从Student派生一个PrimaryStudent需要编写的代码量。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:

class PrimaryStudent extends Student {
constructor(name, grade) {
super(name); // 记得用super调用父类的构造方法!
this.grade = grade;
}

myGrade() {
    alert('I am at grade ' + this.grade);
}

}
注意PrimaryStudent的定义也是class关键字实现的,而extends则表示原型链对象来自Student。子类的构造函数可能会与父类不太相同,例如,PrimaryStudent需要name和grade两个参数,并且需要通过super(name)来调用父类的构造函数,否则父类的name属性无法正常初始化。

PrimaryStudent已经自动获得了父类Student的hello方法,我们又在子类中定义了新的myGrade方法。

ES6引入的class和原有的JavaScript原型继承有什么区别呢?实际上它们没有任何区别,class的作用就是让JavaScript引擎去实现原来需要我们自己编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。

你一定会问,class这么好用,能不能现在就用上?

现在用还早了点,因为不是所有的主流浏览器都支持ES6的class。如果一定要现在就用上,就需要一个工具把class代码转换为传统的prototype代码,可以试试Babel这个工具。

}

7.1 创建对象

JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。

当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。

例如,创建一个Array对象:

var arr = [1, 2, 3];
其原型链是:

arr ----> Array.prototype ----> Object.prototype ----> null
Array.prototype定义了indexOf()、shift()等方法,因此你可以在所有的Array对象上直接调用这些方法。

当我们创建一个函数时:

function foo() {
return 0;
}
函数也是一个对象,它的原型链是:

foo ----> Function.prototype ----> Object.prototype ----> null
由于Function.prototype定义了apply()等方法,因此,所有函数都可以调用apply()方法。

很容易想到,如果原型链很长,那么访问一个对象的属性就会因为花更多的时间查找而变得更慢,因此要注意不要把原型链搞得太长。

构造函数

除了直接用{ ... }创建一个对象外,JavaScript还可以用一种构造函数的方法来创建对象。它的用法是,先定义一个构造函数:

function Student(name) {
    this.name = name;
    this.hello = function () {
        alert('Hello, ' + this.name + '!');
    }
}

在JavaScript中,可以用关键字new来调用这个函数,并返回一个对象:

var xiaoming = new Student('小明');
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!

注意,如果不写new,这就是一个普通函数,它返回undefined。但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;。

新创建的xiaoming的原型链是:

xiaoming ----> Student.prototype ----> Object.prototype ----> null
也就是说,xiaoming的原型指向函数Student的原型。如果你又创建了xiaohong、xiaojun,那么这些对象的原型与xiaoming是一样的:

xiaoming ↘
xiaohong -→ Student.prototype ----> Object.prototype ----> null
xiaojun ↗
用new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身:

xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true
Object.getPrototypeOf(xiaoming) === Student.prototype; // true
xiaoming instanceof Student; // true

现在我们就认为xiaoming、xiaohong这些对象“继承”自Student。

不过还有一个小问题,注意观察:

xiaoming.name; // '小明'
xiaohong.name; // '小红'
xiaoming.hello; // function: Student.hello()
xiaohong.hello; // function: Student.hello()
xiaoming.hello === xiaohong.hello; // false

xiaoming和xiaohong各自的name不同,这是对的,否则我们无法区分谁是谁了。

xiaoming和xiaohong各自的hello是一个函数,但它们是两个不同的函数,虽然函数名称和代码都是相同的!

如果我们通过new Student()创建了很多对象,这些对象的hello函数实际上只需要共享同一个函数就可以了,这样可以节省很多内存。

要让创建的对象共享一个hello函数,根据对象的属性查找原则,我们只要把hello函数移动到xiaoming、xiaohong这些对象共同的原型上就可以了,也就是Student.prototype:

protos2

修改代码如下:

function Student(name) {
this.name = name;
}

Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
};

用new创建基于原型的JavaScript的对象就是这么简单!

忘记写new怎么办

如果一个函数被定义为用于创建对象的构造函数,但是调用时忘记了写new怎么办?

在strict模式下,this.name = name将报错,因为this绑定为undefined,在非strict模式下,this.name = name不报错,因为this绑定为window,于是无意间创建了全局变量name,并且返回undefined,这个结果更糟糕。

所以,调用构造函数千万不要忘记写new。为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写,这样,一些语法检查工具如jslint将可以帮你检测到漏写的new。

最后,还可以编写一个createStudent()函数,在内部封装所有的new操作。一个常用的编程模式像这样:

function Student(props) {
    this.name = props.name || '匿名'; // 默认值为'匿名'
    this.grade = props.grade || 1; // 默认值为1
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
};

function createStudent(props) {
    return new Student(props || {})
}

这个createStudent()函数有几个巨大的优点:一是不需要new来调用,二是参数非常灵活,可以不传,也可以这么传:

var xiaoming = createStudent({
name: '小明'
});

xiaoming.grade; // 1
如果创建的对象有很多属性,我们只需要传递需要的某些属性,剩下的属性可以用默认值。由于参数是一个Object,我们无需记忆参数的顺序。如果恰好从JSON拿到了一个对象,就可以直接创建出xiaoming。

// out: 345

4.4 sort()

Array的sort()方法默认把所有元素先转换为String再排序
sort()方法会直接对Array进行修改,它返回的结果仍是当前Array
sort()方法也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序。
如果要按数字大小排序,可以这么写:

var arr = [10, 20, 1, 2];
arr.sort(function (x, y) {
    if (x < y) {
        return -1;
    }
    if (x > y) {
        return 1;
    }
    return 0;
}); // [1, 2, 10, 20]

// out: hello

三、函数

在JavaScript中,定义函数的方式如下:

function abs(x) {
...
return ...;
}
如果没有return,返回结果为undefined。

第二种定义函数的方式如下:

var abs = function (x) {
return ...;
};
在这种方式下,function(x){...}是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量abs,通过变量abs就可以调用该函数。注意这种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。
第三种定义函数的方式:自执行函数
(function(arg){
console.log(arg);
})('123');
这种方式下,函数在定义的同时就执行了,常用于多个js文件时封装自身或闭包。说白了,就是获得一块命名空间,不影响他人或被他人影响。

JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多或少都没有问题。这与其他的语言打不相同,个人认为也许是设计时间较短,先天不足的问题。

arguments关键字

关键字arguments只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array。
利用arguments,可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值。
实际上arguments最常用于判断传入参数的个数。

rest参数

由于JavaScript函数允许接收任意个参数,于是不得不用arguments来获取所有参数。为了获取除了已定义参数之外的参数,ES6标准引入了rest参数。

function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}

foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

foo(1);
// 结果:
// a = 1
// b = undefined
// Array []

rest参数只能写在最后,前面用...标识,从运行结果可知,传入的参数先绑定a、b,多余的参数以数组形式交给变量rest,所以,不再需要arguments我们就获取了全部参数。

但是,这些参数的特性,个人建议不要使用。因为它那点可怜的帮助,远比它可能带来的风险和麻烦要小。请尽量使用规范和标准的参数传递方法,让他人能看懂,让程序能看懂,而不是炫耀那些无聊的技巧。

当函数被封装在对象中,就称为该对象的方法。
比如:

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var y = new Date().getFullYear();
        return y - this.birth;
    }
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了

在方法内部,this是一个特殊变量,它始终指向当前对象,也就是xiaoming这个变量。所以,this.birth可以拿到xiaoming的birth属性。

但是由于js设计的先天缺陷,在语言的初期就存在很多不足或考虑欠佳的地方,关于方法的调用有各种坑,因此不是非常有必要的情况下,请使用最基本的语法,不要炫耀一些冷门的技巧。如果你非要,请研究apply和call方法,或者js的装饰器。

var factorial = function(n) {

4. JavaScript的作用域链执行前已创建

JavaScript的作用域在被执行之前已经创建,日后再去执行时只需要按照作用域链去寻找即可。

例子1:

xo = 'alex';
function Func(){
    var xo = "seven";
    function inner(){
        console.log(xo);
    }
    return inner;
}
var ret = Func();
ret();
// 输出结果: seven

上述代码,在函数被调用之前作用域链已经存在:
全局作用域 -> Func函数作用域 -> inner函数作用域
当执行【ret();】时,由于其代指的是inner函数,此函数的作用域链在执行之前已经被定义为:全局作用域 -> Func函数作用域 -> inner函数作用域,所以,在执行【ret();】时,会根据已经存在的作用域链去寻找变量。

例子2:

xo = 'alex';
function Func(){
    var xo = "eirc";
    function inner(){
        console.log(xo);
    }
    xo = 'seven';
    return inner;
}
var ret = Func();
ret();
// 输出结果: seven

上述代码和示例一的目的相同,也是强调在函数被调用之前作用域链已经存在:
全局作用域 -> Func函数作用域 -> inner函数作用域
不同的时,在执行【var ret = Func();】时,Func作用域中的xo变量的值已经由 “eric” 被重置为 “seven”,所以之后再执行【ret();】时,就只能找到“seven”。

例子3:

xo = 'alex';<br>
function Bar(){
    console.log(xo);
}
function Func(){
    var xo = "seven";

    return Bar;
}
var ret = Func();
ret();
// 输出结果: alex

上述代码,在函数被执行之前已经创建了两条作用域链:
全局作用域 -> Bar函数作用域
全局作用域 -> Func函数作用域
当执行【ret();】时,ret代指的Bar函数,而Bar函数的作用域链已经存在:全局作用域 -> Bar函数作用域,所以,执行时会根据已经存在的作用域链去寻找。

根据这个思路和前面提到的传统方法, 私有变量和类的属性, 方法都是很简单的事情.

4.5 闭包

在没有class机制,只有函数的语言里,借助闭包,同样可以封装一个私有变量。我们用JavaScript创建一个计数器:

'use strict';
function create_counter(initial) {
    var x = initial || 0;
    return {
        inc: function () {
            x += 1;
            return x;
        }
    }
}

它用起来像这样:

var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3

var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。

闭包还可以把多参数的函数变成单参数的函数。例如,要计算xy可以用Math.pow(x, y)函数,不过考虑到经常计算x2或x3,我们可以利用闭包创建新的函数pow2和pow3:

function make_pow(n) {
    return function (x) {
        return Math.pow(x, n);
    }
}

// 创建两个新函数:
var pow2 = make_pow(2);
var pow3 = make_pow(3);

pow2(5); // 25
pow3(7); // 343

a[a.length] = 3;

7.2 原型继承

在传统的基于Class的语言如Java、C++中,继承的本质是扩展一个已有的Class,并生成新的Subclass。

由于这类语言严格区分类和实例,继承实际上是类型的扩展。但是,JavaScript由于采用原型继承,我们无法直接扩展一个Class,因为根本不存在Class这种类型。

但是办法还是有的。我们先回顾Student构造函数:

function Student(props) {
this.name = props.name || 'Unnamed';
}

Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
}
以及Student的原型链:

js-proto

现在,我们要基于Student扩展出PrimaryStudent,可以先定义出PrimaryStudent:

function PrimaryStudent(props) {
// 调用Student构造函数,绑定this变量:
Student.call(this, props);
this.grade = props.grade || 1;
}
但是,调用了Student构造函数不等于继承了Student,PrimaryStudent创建的对象的原型是:

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null
必须想办法把原型链修改为:

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null
这样,原型链对了,继承关系就对了。新的基于PrimaryStudent创建的对象不但能调用PrimaryStudent.prototype定义的方法,也可以调用Student.prototype定义的方法。

如果你想用最简单粗暴的方法这么干:

PrimaryStudent.prototype = Student.prototype;
是不行的!如果这样的话,PrimaryStudent和Student共享一个原型对象,那还要定义PrimaryStudent干啥?

我们必须借助一个中间对象来实现正确的原型链,这个中间对象的原型要指向Student.prototype。为了实现这一点,参考道爷(就是发明JSON的那个道格拉斯)的代码,中间对象可以用一个空函数F来实现:

// PrimaryStudent构造函数:
function PrimaryStudent(props) {
Student.call(this, props);
this.grade = props.grade || 1;
}

// 空函数F:
function F() {
}

// 把F的原型指向Student.prototype:
F.prototype = Student.prototype;

// 把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype:
PrimaryStudent.prototype = new F();

// 把PrimaryStudent原型的构造函数修复为PrimaryStudent:
PrimaryStudent.prototype.constructor = PrimaryStudent;

// 继续在PrimaryStudent原型(就是new F()对象)上定义方法:
PrimaryStudent.prototype.getGrade = function () {
return this.grade;
};

// 创建xiaoming:
var xiaoming = new PrimaryStudent({
name: '小明',
grade: 2
});
xiaoming.name; // '小明'
xiaoming.grade; // 2

// 验证原型:
xiaoming.proto === PrimaryStudent.prototype; // true
xiaoming.proto.proto === Student.prototype; // true

// 验证继承关系:
xiaoming instanceof PrimaryStudent; // true
xiaoming instanceof Student; // true
用一张图来表示新的原型链:

js-proto-extend

注意,函数F仅用于桥接,我们仅创建了一个new F()实例,而且,没有改变原有的Student定义的原型链。

如果把继承这个动作用一个inherits()函数封装起来,还可以隐藏F的定义,并简化代码:

function inherits(Child, Parent) {
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}
这个inherits()函数可以复用:

function Student(props) {
this.name = props.name || 'Unnamed';
}

Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
}

function PrimaryStudent(props) {
Student.call(this, props);
this.grade = props.grade || 1;
}

// 实现原型继承链:
inherits(PrimaryStudent, Student);

// 绑定其他方法到PrimaryStudent原型:
PrimaryStudent.prototype.getGrade = function () {
return this.grade;
};
小结

JavaScript的原型继承实现方式就是:

定义新的构造函数,并在内部用call()调用希望“继承”的构造函数,并绑定this;

借助中间函数F实现原型链继承,最好通过封装的inherits函数完成;

继续在新的构造函数的原型上定义新方法。

 

JavaScript之旅(三)


在class-based语言中, 有的属性可以直接通过类名使用, 并且一个类的所有对象共享同一个对象. 在javascript因为所有的函数本身就是对象, 构造函数也不例外, 所以可以通过在构造函数上直接添加属性来实现这样的特性.

5.声明提前

在JavaScript中如果不创建变量,直接去使用,则报错:
console.log(xxoo);
// 报错:Uncaught ReferenceError: xxoo is not defined
JavaScript中如果创建值而不赋值,则该值为 undefined,如:
var xxoo;
console.log(xxoo);
// 输出:undefined
在函数内如果这么写:
function Foo(){
console.log(xo);
var xo = 'seven';
}
Foo();
// 输出:undefined
上述代码,不报错而是输出undefined,其原因是:JavaScript的函数在被执行之前,会将其中的变量全部声明,而不赋值。所以,相当于上述实例中,函数在“预编译”时,已经执行了varxo;所以上述代码中输出的是undefined。

实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性。
JavaScript实际上只有一个全局作用域。任何变量(函数也视为变量),如果没有在当前函数作用域中找到,就会继续往上查找,最后如果在全局作用域中也没有找到,则报ReferenceError错误。
全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。
减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。把自己的代码全部放入唯一的名字空间MYAPP中,会大大减少全局变量冲突的可能。
许多著名的JavaScript库都是这么干的:jQuery,YUI,underscore等等。
为了解决块级作用域,ES6引入了新的关键字let,用let替代var可以申明一个块级作用域的变量。
ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域。

console.log( a.sort( function(a, b) { return a - b; } ) );

1. “JavaScript中无块级作用域”

在Java或C#中存在块级作用域,即:大括号也是一个作用域。但是在JavaScript和python中没有块级作用域。

但是!但是!JavaScript6中新引入了let关键字,用于指定变量属于块级作用域。也就是说以后可能会有块级作用域了。

  console.log("hello," + this.name);

4.1 map()

类似python的map函数。

function pow(x) {
    return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]

map()传入的参数是pow,即函数对象本身。

    return count;

// out: hello,world!

 

 

}

 

匿名函数也被称为lambda, 是个很方便和有用的特性, 加上对闭包的支持, 以此衍生了很多特性. 也因此成就了javascript类函数语言的特性.

面向对象

// out: NaN

add(2, 3, 4, 5);

上述两种在数组后面添加元素的方法是等效的, 假如添加的元素不是数组的下一个元素(即跳跃式添加的话), 中间会用undefined填充.

 

支持用label方式的break和continue, 用于在多层循环中直接对外层循环进行break和continue.

 

function add(x, y) {

obj.name = "Simon";

function mul(x, y) {

  var count = base;

// out: 12

console.log( rect.area() );

}

 

 

  return x + y;

             }

 

add();

               rect.perimeter = function() { return this.width * 2 + this.height * 2; };

caller(function(a, b) { console.log(a+b); }, 10, 20);

for (var name in obj) {

私有成员(Private Members)

不过, 上面代码并不完美, 最大的问题在于每个创建的对象都有一个自己的area函数, 而实际上, 所有的对象只需要指向一个共同的area函数即可, 这也是C++等class-based语言的做法. 为每个对象都创建一个函数, 无论是运行效率还是内存占用效率都不恰当. javascript提供的解决方案就是prototype, 在函数对象中默认都会初始化一个prototype的变量, 这个变量中有的所有函数最终都会被这个函数新创建的对象拥有, 并且拥有的还都是引用, 见代码:

 

console.log(rect.area());

 

    for (var i = 0, j = arguments.length; i < j; i++) {

  if (n <= 1) {

因为匿名函数和递归在javascript中使用的都比一般语言要多, 所以提供了arguments.callee用于表示当前调用的函数, 以方便匿名函数的递归调用, 事实上, 相对一般用函数名的递归调用方式, 这种方式要更加符合DRY(Dont Repeat Yourself)原则, 因为当函数名更改的时候, 不用再更改递归调用的函数名了.

上面的Rectange类, 可以改为下面的方式实现.

 var rect = Rectangle.createNew(2, 3);

Rectangle.prototype.area = function() {

// out: 24

类方法(Class Methods)

语法细节

// out: 5

其实也不是完全没有用过javascript, 以前在开发一个Unity项目的时候用过一下Unity里面的javascript, 只不过那个javascript我甚至都只能称之为UnityScript. 太多太多自己实现的特性, 而又有些不够完整. 现在, 认识一下真正的javascript吧.

需要解释一下, node跟浏览器里嵌入的javascript不一样, 不具有类似confirm和prompt等接口, 我用console.log来输出.

 

基于原型的继承

console.log(rect.perimeter());

上述的代码在现在已经不稀奇了, 但是相对C++来说还是更先进的.(可见C++多落后了)

}

 

}

 

}

 var rect = Rectangle.createNew(2, 3);

自定义对象:

动态类型语言, 变量通过var定义.

 

                rect.area = function() { return this.width * this.height; };

"hello, world".length

        sum += arguments[i];

function add(x, y) {

console.log(rect1.are());

数组

// out: 2

   createNew: function(w, h) {

 

}

var SubRectangle = {

               var rect = Rectangle.createNew(w, h);

}

在乘法运算中, 因为javascript的字符串并没有像Ruby, Python一样对乘法的运算做出特殊解释(字符串的乘法表示重复), 所以默认会将字符串转为整数进行运算, 更诡异的是, 就算是两个字符串, 同样也会不报错的进行整数转换并且运算.

此时, 无论是在对象外还是在对象内部, 都只能通过访问函数(getWidth和getHeight)获得成员变量.

// out: 6

 

}

类属性(Class Properties)

                return rect;

 

与大部分语言一样, javascript也分为原生类型和引用类型, 其中原生类型在拷贝, 参数传递和比较时时通过值的方式, 而引用类型都是通过引用的方式.

 

 

  this.height = h;

 

var obj = new Object();

 

    console.log(j);

                var width = w;    // private

可选的语句结束符;, 这个很少见. 不过一般的规范都推荐不要真的省.

类的创建

// out: 8

    return sum;

function Rectangle(w, h) {

 

事实上, 上面的简单办法没有在根本上解决问题, 只是限定需要通过访问函数了而已, 外部还是能访问对象的内部变量. Crckford据说是第一个发现了javascript创建真正私有变量的技术. 该技术主要在Private Members in JavaScript有描述.

}

 

 

  name : "Simon",

这种继承方式和class-based的继承不一样, 直接使用了javascript的prototype特性, 在真正的class没有出来之前, 我个人对这样的方式好感更多. 主要可以参考的还是Douglas Crockford的文章, 见Prototypal Inheritance in JavaScript

 

  }

}

javascript虽然说语法是类C的, 但是起点是Java, 所以尽管设计的面向对象系统虽然不是传统的模版式的, 但是javascript中的字符串都是对象.

  else {

此方法来自JavaScript The Definitive Guide, 代码如下:

当javascript 2.0加入了class以后, 可能将来使用javascript就和C++等语言区别不大了. 可能会更像UnityScript.

}

    return x * 10;

 

  createNew: function(w, h) {

                var height = h;   // private

  hello : function() {

Rectangle.prototype.area = function() {  return this.width * this.height; };

 

// [ 0, 1, 2, 3, 4 ]

 

function Rectangle(w, h) {

  return function(num) {

 

  var width = w;

var sub = function(x, y) {

 

匿名函数

// out: NaN

add(2, 3, 4);

// out: 22

本质上是因为一旦函数调用参数不够时, 后面的参数都会被置为undefined. 所以虽然javascript不支持默认参数, 但是可以模拟出来.

一个闭包就是一个函数和被创建的函数中的范围对象的组合. 因为闭包的强大特性和带来的方便, 很多传统的语言都逐渐了加入了对其的支持, 很多时候, 甚至被视为一个语言是否还算是跟上时代的标志.

我在新的SubRectangle子类中新增了perimeter函数, 用于计算周长, 可以看到使用的方法和传统的继承非常的像.

和类属性一样, 在构造函数上创建对象, 就能模拟出class-based语言中类方法. 这里不累述了.

正因为其实对象就是一个关联数组, 所以同样可以用for in来遍历, 作用就像是python中的dir一样. 类似这种自审视的功能, 在传统静态语言是较为稀缺的, 在那种语言里这种功能叫做反射. 相配套的还有typeof操作符, hasOwnProperty, propertyIsEnumerable, isPrototypeof函数.

javascript的数组比想象的要灵活, 支持用超出索引的引用来添加元素, 这个我只在ruby和php中见过, 连python都不支持. 当然, 这种设计虽然灵活, 但是容易出现很隐晦的错误, 最终是好是坏也难以评价.

    count += num;

因为我还是不准备使用这个方法, 所以这里还是不加描述了.

   createNew: function(w, h) {

               rect.area = Rectangle._area; 

 

 

 

console.log("3" * "4");

}

 

a = [1, 3, 2, 10, 20];

  }

console.log(a);

闭包

var rect = SubRectangle.createNew(2, 3);

灵活的参数调用

这个方法能解决函数有多份的问题, 但是同时带来的问题就是无法访问私有成员, 同时会给外部的Rectangle增加一些接口, 虽然可以通过命名来告诉调用者, 这些接口是私有的. 具体用哪种方法, 就看是注重效率还是注重代码本身的设计了.

}

    return this.width * this.height; 

 

 

console.log(i);   // 此时i仍然可用.

有上述两种函数构造形式, 在调用时没有区别. 其中第一种方法和传统的函数定义方式一样, 而第二种实际上就是匿名函数的定义方式了. 只不过因为javascript中函数是第一类值, 所以可以很方便的赋值.

更有意思的是, arguments.callee在javascript的严格模式中是禁止的, 简单的说就是这种调用方法是官方不推荐使用的错误用法, 在将来甚至有可能废除, mozilla的解释是这种更DRY的用例本身很”weak”, 但是却阻止了inline优化的进行, 因为这种方式是通过引用un-inlined函数实现的, 也只有函数un-inlined时, arguments.callee才可以引用到.

该方法我第一次是在阮一峰的网络日志上看到的, 见Javascript定义类(class)的三种方法.

支持自增++,自减符号--, 相对Ruby, Python来说, 这个要更习惯.

javascript算是第一个让大家知道这个世界上除了从C++一派来的class-based(模版式)的类定义方式, 还有类似self语言的prototype(原型)方式的流行语言. 虽然lua也是prototype方式, 但是毕竟只在游戏圈子里面流行.

如上例所示, 匿名函数很重要的一个应用就是用于很方便的构建高阶函数. 也许上例有些太生造, 最常用的一个特性可能就是排序了, 因为排序的规则可能很多, 一般排序函数都允许再传入一个函数作为参数, 来指定排序的规则. 比如再javascript中, 普通的排序函数有些奇怪, 默认是按照字符串排序的. 见下例:

 

console.log(rect1.are());

 

              }

需要注意的是, 此时的Rectangle是一个单纯的对象, 而不再如传统方式一样是一个函数(当然, 其实也是对象). 这也是一个更容易理解的优点. 然后, 我们可以简单的在子类的createNew函数中先创建出要继承的对象, 然后继续修改该对象直到达到我们的要求. 如下:

  createNew: function(w, h) { 

  this.getWidth = function() { return w; }

 

sub(5, 3);

(function() {

 

 

add(2);

简单的方法

              }

function add() {

 

 }

  for (var j = 0; j < 10; ++j) {

支持NaN, null, undefined这三种表示类似无意义的量的方式, 有的时候这是混乱的根源. 也许还要再加上Infinity.

 

 }

// out: 8

 

 

以上代码用类似构造函数的方式处创建了两个类型为Rectangle的对象. 注意和以前创建对象的区别, 以前我们都是从Object直接开始构建, 那样在构建多个对象时远不如这种构造函数方式方便. 用这种方法, 我们就能得到简单的类似class-based对象创建的方法. 只是创建的是构造函数, 不是一个class而已.

私有成员及类的属性

 

               var rect = {}; 

 

概要

console.log(4 + 5 + "3");

// out:  100

factorial(4);

 

 

  this.width = w;

在class-based语言中(Python例外), 一般都有对不同的成员设置不同访问权限的方法. 比如C++的prvate, public, protected, 在javascript, 通过上述方式创建的对象, 你可以看成都是默认为public的, 但是也的确有办法让外部访问不了内部的变量.

                rect.height = h;

console.log("3" + 4 + 5);

 

语言的发展道路基本上是趋同的, 程序社区有较为公认的标准, 所以PHP也在新的版本中加入了完整的面向对象支持, 而C++在11标准里面加入了闭包. 而Java和C#在新版本中不仅加入了闭包, 还增加了模版.

}

Crockford的办法

}

 

var obj = {

javascript的对象本质上就是一个hash表的集合.

console.log("3" * 4);

 

 

var obj = new Object();

  var height = h;

更进一步, 你甚至可以通过obj["hello"]()这种调用关联数组的方式来调用对象中的函数.

  return x * y;

 

add(2, 3);

mul(10);

 

    return 1;

 

 

 

               rect.height = h; 

// out: name

// out: 11

 

有上述两种语法用于创建空对象, 其中第二种被称为’literal’语法, 也是常用的数据格式Json的基础.

 

也就是因为设计比较简单, JavaScript也被一些人认为不算是现代语言, 不具有现代语言的一些特性.

}

obj1(1);

函数在javascript中是第一类值, 同时还支持闭包. 这是javascript构成对象的基础.

 

  }

console.log(rect.area());

函数级作用域

 })();

console.log(str);

 

字符串为不可变类型, 任何的改变处理都是生成新字符串. 比较时为值比较. 字符串的值比较我个人认为时更加自然的做法, 比Java那种变态的方式要自然的多. 你几乎要反复的告诉每一个新来的程序员, 字符串的值比较在java中要使用equals函数.

                rect.area = function() { return width * height; };

  if (y === undefined) {

                var rect = {};

简单的说就是子类讲自己的prototype变量设为想要继承的父类对象, 根据javascript的特性, 当一个函数找不到时, 会在prototype中寻找, 也就相当于子类没有重载时直接使用父类的函数. 当一个函数可以找到时, 会直接使用子类的函数, 这相当于子类重载了父类的相关函数.

有些不一样的是, 因为javascript中函数是第一类值, 所以可以很自然的在这个对象中添加函数, 完成完整的数据封装. 用{}来初始化上述对象的话, 会更加简单:

就阮一峰描述, 该方法最先由荷兰人Gabor de Mooij在Object oriented programming in Javascript中提出.

                rect.width = w;

var Rectangle = { 

  return x - y;

 

             } 

 

var rect = new Rectangle(2, 3);

 

 

上述代码在调用时不会发生错误, 而是直接把后面的参数抛弃掉.

    console.log("hello," + this.name);

  console.log(name);

 

obj1 = makeIncrementor(10);

 

// out: [ 1, 10, 2, 20, 3 ]

该方法不使用Object.create和prototype特性, 本质上是新增一个自己约定的构造函数, 自己模拟了一个类似prototype的原型机制, 代码相对来说比较简单容易理解. 但是其实已经颠覆了本节前面提到的所有内容, 比如没有使用prototype和new.

Rectangle.UNIT = new Rectangle(1, 1);

更诡异的还不只这些, 对于加法来说是如此, 对于乘法来说又是另外一回事:

var caller = function(fun, leftParam, rightParam) {

switch和传统的C语言语法类似, 但是可以支持字符串的case.

更灵活的语法是可以通过arguments变量来获取参数, 这样可以支持任意数量的函数参数.

 

 

var obj2 = {};

甚至于, 后面的两个参数不够的函数调用, 会返回NaN, 也不会发生错误.

               rect.width = w; 

 

  }

  this.height = h;

obj2(2);

a = [1, 3, 2, 10, 20];

 Rectangle.UNIT = Rectangle.createNew(1, 1); // class propertie

 console.log(rect.area());

    }

 

  this.area = function() { return this.width * this.height; }

  return this.getWidth() * this.getHeight();

共用函数

console.log(j); // ReferenceError: j is not defined.

  this.area = function() {

obj.hello();

 

obj.hello = function() {

, 从Ruby到Lisp, 然后是C#, 既然已经疯狂了, 就顺面学习一下javascript吧. 对javascript的印象一直不佳, 从骂脏话最多的使用...

 

// out: 12

  return x + y;

 

 

 

这个方法总的来说非常简单直观和直接, 没有必要像Douglas Crockford的方法一样需要创建较多的辅助函数来实现. 但是, 其实还是像前面没有用到prototype的解决方法一样, 每个对象的成员函数都是独立的, 假如对效率和内存比较在意的话, 可以使用引用外部函数的方法来优化. 如下:

var rect1 = new Rectangle(2, 4);

 console.log(rect.area());

 

var Rectangle = {

    return n * arguments.callee(n - 1);

// out: 12

// out: 30

  this.getHeight = function() { return h; }

 

 

 

 console.log(Rectangle.UNIT.area());

  }

对象

var Rectangle = {

Mac OS X 10.8.2, node v0.8.16

上面的例子较好的展示了闭包的特性, 可以获得上层函数的参数和变量, 并且各自互相独立, 因为闭包对局部状态的保存, 很多时候能当作一个对象来使用.

  }

 

 

 

// out: 5

function makeIncrementor(base) {

函数

console.log(a.sort());

 

 

obj2 = makeIncrementor(20);

字符串

obj2(2);

 

相关内容见Douglas Crockford的Classical Inheritance in JavaScript. 我个人因为对这种奇怪的方式比较反感, 所以不太想去使用, 这里就不进行描述了. 需要提及的是, 假如真的需要class-based的继承的话, 在最新版的javascript 2.0规范(ECMAScript 5)中你能找到你想要的真正的类. 虽然相关标准还在进行当中, 可能还需要几年才能实际使用.

 

a.push(4);

 

for (var i = 0; i < 10; ++i) {

 

这在大部分时候估计都不是我们要的做法, 默认这样子我是第一次看见, 这就像字符串和整数想加最后变成字符串一样诡异, 也许javascript本身设计的时候是作为前端检验表单啥为主的语言, 所以对字符串这么偏爱吧. 幸运的是, sort函数还是可以传入一个函数作为排序规则的. 见下例:

 

// out: 14

其实, 类似的用法javascript本身就有, 比如Number.MAX_VALUE就是这样的类属性.

 

// out: hello,Simon.

    return width * height;

该方法利用了javascript的闭包特性, 此时width和height在外部彻底无法访问. 只有函数内部才能访问. 同样的, 私有的函数也可以通过一样的方法实现, 但是, 这个方法我感觉还是不够完美, 因为很显然的原因, 此时需要访问私有变量的函数都只能在构造函数中直接定义, 不能再使用prototype变量了, 也就是会有前面提到的每个对象都会有个新函数的问题.

                var rect = {};

var rect2 = new Rectangle(8.5, 11);

var rect = new Rectangle(2, 3);

模拟class-based的继承

function Rectangle(w, h) {

  fun(leftParam, rightParam);

 

 

 

a = [0, 1, 2];

                return rect;

 

 

字符串支持+操作符作为字符串连接.

  this.width = w;

 

// out: 12

 

javascript有个奇怪的地方是字符串和数字同时使用时:

  _area: function() { 

// out: [ 1, 2, 3, 10, 20 ]

}

完整并且传统的try, catch, finally异常机制. 除了C++没有finally不够完整以外, 几乎所有现在语言的异常都是这么设计的了.

JavaScript本身就是设计为一个前端语言, 据说设计只用了10天, 有些缺陷, 但是的确足够简单. 虽然JavaScript The Definitive Guide和大多数的语言书籍一样厚如砖头, 但是其实语言本身的介绍只有前面近200页, 这个厚度其实也就和R&D中描述的C语言差不多.

 

 

  }

 

function Rectangle(w, h) {

               return rect;

               return rect; 

var str = "hello" + "," + "world!";

 

  console.log(i);

极简主义法

// out: 93

 

    var sum = 0;

 

事实上, 我觉得这简直是因噎废食的做法, 因为现在虽然是这样实现的, 但是完全可以通过更好的语法分析, 然后进行编译器的优化, 而不是因此废弃这样有用的语法. 这种用法绝对不像是官方说的那么”weak”, 要知道, DRY几乎是软件设计领域头等重要的原则.

obj1(1);

本文由金沙棋牌发布于金沙棋牌官方平台,转载请注明出处:理解JavaScript的原型属性,的原型属性

关键词:

上一篇:没有了

下一篇:没有了