什么是虚拟视窗,进化与性能

组合使用原型链和借用构造函数

通常,我们会组合使用原型链继承和借用构造函数来实现继承。也就是说,使用原型链实现对原型属性和方法的继承,
而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。
我们改造最初的例子如下:

JavaScript

// 父类构造函数 function Person (name, age, job) { this.name = name;
this.age = age; this.job = job; } // 父类方法 Person.prototype.sayName =
function () { console.log(this.name); }; // ————– //
子类构造函数 function Student (name, age, job, school) { //
继承父类的所有实例属性(获得父类构造函数中的属性) Person.call(this,
name, age, job); this.school = school; // 添加新的子类属性 } //
继承父类的原型方法(获得父类原型链上的属性和方法) Student.prototype =
new Person(); // 新增的子类方法 Student.prototype.saySchool = function
() { console.log(this.school); }; var person1 = new Person(‘Weiwei’, 27,
‘Student’); var student1 = new Student(‘Lily’, 25, ‘Doctor’, “Southeast
University”); console.log(person1.sayName === student1.sayName); // true
person1.sayName(); // Weiwei student1.sayName(); // Lily
student1.saySchool(); // Southeast University

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 父类构造函数
function Person (name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
}
// 父类方法
Person.prototype.sayName = function () {
  console.log(this.name);
};
// ————–
// 子类构造函数
function Student (name, age, job, school) {
  // 继承父类的所有实例属性(获得父类构造函数中的属性)
  Person.call(this, name, age, job);
  this.school = school; // 添加新的子类属性
}
// 继承父类的原型方法(获得父类原型链上的属性和方法)
Student.prototype = new Person();
// 新增的子类方法
Student.prototype.saySchool = function () {
  console.log(this.school);
};
var person1 = new Person(‘Weiwei’, 27, ‘Student’);
var student1 = new Student(‘Lily’, 25, ‘Doctor’, "Southeast University");
console.log(person1.sayName === student1.sayName); // true
person1.sayName();  // Weiwei
student1.sayName(); // Lily
student1.saySchool(); // Southeast University

组合集成避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为了JavaScript中最常用的继承模式。
而且,instanceofisPropertyOf()也能够用于识别基于组合继承创建的对象。

html { overflow: hidden; }

这给开发人员带来的最主要变化是:在M39中,将overflow属性值设置为hidden后页面仍然能够滚动,但是在M40中,这样做不再有效。

为什么说 JavaScript 数组不是真正的数组

在聊 JavaScript 之前,先讲讲 Array 是什么。

数组是一串连续的内存位置,用来保存某些值。注意重点,“连续”(continuous,或
contiguous),这很重要。

图片 1

上图展示了数组在内存中存储方式。这个数组保存了 4 个元素,每个元素 4
字节。加起来总共占用了 16 字节的内存区。

假设我们声明了 tinyInt arr[4];,分配到的内存区的地址从 1201
开始。一旦需要读取 arr[2],只需要通过数学计算拿到 arr[2]
的地址即可。计算 1201 + (2 X 4),直接从 1209 开始读取即可。

图片 2

JavaScript
中的数据是哈希映射,可以使用不同的数据结构来实现,如链表。所以,如果在
JavaScript 中声明一个数组
var arr = new Array(4),计算机将生成类似上图的结构。如果程序需要读取
arr[2],则需要从 1201 开始遍历寻址。

以上急速 JavaScript
数组与真实数组的不同之处。显而易见,数学计算比遍历链表快。就长数组而言,情况尤其如此。

关键字:super

super关键字用于调用父对象上的函数。
super.propsuper[expr]表达式在类和对象字面量中的任何方法定义中都有效。

JavaScript

super([arguments]); // 调用父类构造器
super.functionOnParent([arguments]); // 调用父类中的方法

1
2
super([arguments]); // 调用父类构造器
super.functionOnParent([arguments]); // 调用父类中的方法

如果是在类的构造器中,需要在this关键字之前使用。参考链接

什么是虚拟视窗(virtual viewport)

2015/04/09 · HTML5 ·
虚拟视窗

本文由 伯乐在线 –
柒柒
翻译,周进林
校稿。未经许可,禁止转载!
英文出处:updates.html5rocks.com。欢迎加入翻译组。

尽管谷歌新推出的移动浏览器Chrome
M40在视窗上做的改变非常微小,但这对用户来说却大有不同。

在启动移动浏览器时,不加视窗元标签的情况下,浏览器的网页大小默认为屏幕实际大小的980px左右,并在此基础上进行渲染。而加上视窗元标签的话,开发人员可以自定义网页宽度,通常设置为“设备宽度”,就是让页面大小自适应于设备的屏幕宽度。详见learn
more on Web
Fundamentals。

Rick
Byers这样描述虚拟视窗:虚拟视窗就是将“视窗”概念分割成两部分,一个是“布局视窗(layout
viewpor)”(在这里,所有的内容都处于固定的位置上),另一个是“虚拟视窗(visual
viewport)”(用户实际看见的部分)。

旧式数组:插入

var LIMIT = 10000000; var arr = new Array(LIMIT); console.time(“Array
insertion time”); for (var i = 0; i< LIMIT; i++) { arr[i] = i; }
console.timeEnd(“Array insertion time”);

1
2
3
4
5
6
7
var LIMIT = 10000000;
var arr = new Array(LIMIT);
console.time("Array insertion time");
for (var i = 0; i< LIMIT; i++) {
arr[i] = i;
}
console.timeEnd("Array insertion time");

用时:55ms

深入解读 JavaScript 中的面向对象编程

2017/07/07 · JavaScript
·
面向对象

原文出处: 景庄   

面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式,主要包括模块化、多态、和封装几种技术。
对 JavaScript
而言,其核心是支持面向对象的,同时它也提供了强大灵活的基于原型的面向对象编程能力。
本文将会深入的探讨有关使用 JavaScript
进行面向对象编程的一些核心基础知识,包括对象的创建,继承机制,
最后还会简要的介绍如何借助 ES6
提供的新的类机制重写传统的JavaScript面向对象代码。

超级简单的例子

Vediojs.com这个网站就是个很好的例子,导航栏固定在顶部,并且在其左右两侧都有相关链接。

下面的两排图片对比展示了,对页面进行放大和左右移动时,在两种版本的移动浏览器上分别会发生什么。

上面一排手机用的是Chrome
M39,这个版本没有虚拟视窗功能,而底下的三个界面来自拥有虚拟视窗的Chrome
M40。

图片 3

图片 4

在Chrome
M39中,你放大界面后还能看到导航栏,但是往右挪就看不到导航栏右侧的链接,只能看到网站的logo。

在这点上Chrome
M40(拥有“虚拟视窗”)就不一样了,你可以看到“虚拟视窗”在“布局视窗”中滚动所有内容,这样就能在左右滑动时看到导航栏上右侧的链接。

IE浏览器已经拥有此项功能,这些改进让我们的浏览器在功能上和他们的更加贴近。

深入 JavaScript 数组:进化与性能

2017/09/18 · JavaScript
· 数组

原文出处: Paul
Shan   译文出处:众成翻译   

正式开始前需要声明,本文并不是要讲解 JavaScript
数组基础知识,也不会涉及语法和使用案例。本文讲得更多的是内存、优化、语法差异、性能、近来的演进。

在使用 JavaScript 前,我对 C、C++、C# 这些已经颇为熟悉。与许多 C/C++
开发者一样,JavaScript 给我的第一印象并不好。

Array 是主要原因之一。JavaScript
数组不是连续(contiguous)的,其实现类似哈希映射(hash-maps)或字典(dictionaries)。我觉得这有点像是一门
B 级语言,数组实现根本不恰当。自那以后,JavaScript
和我对它的理解都发生了变化,很多变化。

继承父类:extends

extends关键字可以用于继承父类。使用extends可以扩展一个内置的对象(如Date),也可以是自定义对象,或者是null

关于作者:柒柒

图片 5

翻译是一门高级的语言艺术,需要长期艰苦地学习和实践才能真正得以掌握。微博:@猫屎咖啡在巴黎

个人主页 ·
我的文章 ·
21 ·
  

图片 6

旧式数组 vs 类型化数组:性能

前面已经讨论了 JavaScript
数组的演进,现在来测试现代数组到底能给我们带来多大收益。下面是我在 Mac
上使用 Node.js 8.4.0 进行的一些微型测试结果。

References

  1. 详解Javascript中的Object对象
  2. new操作符
  3. JavaScript面向对象简介
  4. Object.create()
  5. 继承与原型链
  6. Understanding the prototype property in
    JavaScript

    1 赞 8 收藏
    评论

图片 6

更多有用信息

你想了解的更多?

那么,你可以观看下面的幻灯片(幻灯片需要梯子才能查看)或者点击Rick’s
Google+
Post,他在这方面可比我功夫深,你能真正了解到你想知道的。

1 赞 1 收藏
评论

Typed Array:读取

var LIMIT = 10000000; var buffer = new ArrayBuffer(LIMIT * 4); var arr
= new Int32Array(buffer); console.time(“ArrayBuffer insertion time”);
for (var i = 0; i< LIMIT; i++) { arr[i] = i; }
console.time(“ArrayBuffer read time”); for (var i = 0; i < LIMIT;
i++) { var p = arr[i]; } console.timeEnd(“ArrayBuffer read time”);

1
2
3
4
5
6
7
8
9
10
11
12
var LIMIT = 10000000;
var buffer = new ArrayBuffer(LIMIT * 4);
var arr = new Int32Array(buffer);
console.time("ArrayBuffer insertion time");
for (var i = 0; i< LIMIT; i++) {
arr[i] = i;
}
console.time("ArrayBuffer read time");
for (var i = 0; i < LIMIT; i++) {
var p = arr[i];
}
console.timeEnd("ArrayBuffer read time");

用时:27ms

类的静态方法:static

静态方法就是可以直接使用类名调用的方法,而无需对类进行实例化,当然实例化后的类也无法调用静态方法。
静态方法常被用于创建应用的工具函数。参考链接

旧式数组:读取

var LIMIT = 10000000; var arr = new Array(LIMIT); arr.push({a: 22}); for
(var i = 0; i< LIMIT; i++) { //arr[i] = i; p = arr[i]; }
console.timeEnd(“Array read time”);

1
2
3
4
5
6
7
8
var LIMIT = 10000000;
var arr = new Array(LIMIT);
arr.push({a: 22});
for (var i = 0; i< LIMIT; i++) {
//arr[i] = i;
p = arr[i];
}
console.timeEnd("Array read time");

用时:196ms

面向对象的几个概念

在进入正题前,先了解传统的面向对象编程(例如Java)中常会涉及到的概念,大致可以包括:

  • 类:定义对象的特征。它是对象的属性和方法的模板定义。
  • 对象(或称实例):类的一个实例。
  • 属性:对象的特征,比如颜色、尺寸等。
  • 方法:对象的行为,比如行走、说话等。
  • 构造函数:对象初始化的瞬间被调用的方法。
  • 继承:子类可以继承父类的特征。例如,猫继承了动物的一般特性。
  • 封装:一种把数据和相关的方法绑定在一起使用的方法。
  • 抽象:结合复杂的继承、方法、属性的对象能够模拟现实的模型。
  • 多态:不同的类可以定义相同的方法或属性。

在 JavaScript
的面向对象编程中大体也包括这些。不过在称呼上可能稍有不同,例如,JavaScript
中没有原生的“类”的概念,
而只有对象的概念。因此,随着你认识的深入,我们会混用对象、实例、构造函数等概念。

旧式数组:插入(异构)

var LIMIT = 10000000; var arr = new Array(LIMIT); arr.push({a: 22});
console.time(“Array insertion time”); for (var i = 0; i< LIMIT; i++)
{ arr[i] = i; } console.timeEnd(“Array insertion time”);

1
2
3
4
5
6
7
8
var LIMIT = 10000000;
var arr = new Array(LIMIT);
arr.push({a: 22});
console.time("Array insertion time");
for (var i = 0; i< LIMIT; i++) {
arr[i] = i;
}
console.timeEnd("Array insertion time");

用时:1207ms

改变发生在第 3
行,添加一条语句,将数组变为异构类型。其余代码保持不变。性能差异表现出来了,慢了 22 倍

类:class

是JavaScript中现有基于原型的继承的语法糖。ES6中的并不是一种新的创建对象的方法,只不过是一种“特殊的函数”,
因此也包括类表达式和类声明,
但需要注意的是,与函数声明不同的是,类声明不会被提升。
参考链接

结论

类型化数组的引入是 JavaScript
发展历程中的一大步。Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,这些是类型化数组视图,使用原生字节序(与本机相同)。我们还可以使用
DataView
创建自定义视图窗口。希望未来会有更多帮助我们轻松操作 ArrayBuffer 的
DataView 库。

JavaScript 数组的演进非常
nice。现在它们速度快、效率高、健壮,在内存分配时也足够智能。

 

1 赞 1 收藏
评论

图片 6

Object.getPrototypeOf()

根据ECMAScript标准,someObject.[[Prototype]] 符号是用于指派
someObject 的原型。
这个等同于 JavaScript 的 __proto__
属性(现已弃用,因为它不是标准)。
从ECMAScript 5开始, [[Prototype]]
可以用Object.getPrototypeOf()Object.setPrototypeOf()访问器来访问。

其中Object.getPrototypeOf()在所有支持的实现中,这个方法返回[[Prototype]]的值。例如:

JavaScript

person1.__proto__ === Object.getPrototypeOf(person1); // true
Object.getPrototypeOf(person1) === Person.prototype; // true

1
2
person1.__proto__ === Object.getPrototypeOf(person1); // true
Object.getPrototypeOf(person1) === Person.prototype; // true

也就是说,Object.getPrototypeOf(p1)返回的对象实际就是这个对象的原型。
这个方法的兼容性请参考该链接)。

Typed Array:插入

var LIMIT = 10000000; var buffer = new ArrayBuffer(LIMIT * 4); var arr
= new Int32Array(buffer); console.time(“ArrayBuffer insertion time”);
for (var i = 0; i < LIMIT; i++) { arr[i] = i; }
console.timeEnd(“ArrayBuffer insertion time”);

1
2
3
4
5
6
7
8
var LIMIT = 10000000;
var buffer = new ArrayBuffer(LIMIT * 4);
var arr = new Int32Array(buffer);
console.time("ArrayBuffer insertion time");
for (var i = 0; i < LIMIT; i++) {
arr[i] = i;
}
console.timeEnd("ArrayBuffer insertion time");

用时:52ms

擦,我看到了什么?旧式数组和 ArrayBuffer
的性能不相上下?不不不。请记住,前面提到过,现代编译器已经智能化,能够将元素类型相同的传统数组在内部转换成内存连续的数组。第一个例子正是如此。尽管使用了
new Array(LIMIT),数组实际依然以现代数组形式存在。

接着修改第一例子,将数组改成异构型(元素类型不完全一致)的,来看看是否存在性能差异。

借用构造函数继承

借用构造函数(constructor
stealing)的基本思想如下:即在子类构造函数的内部调用超类型构造函数。

JavaScript

function Father (name) { this.name = name; this.colors = [‘red’,
‘blue’, ‘green’]; } function Child (name) { //
继承了Father,同时传递了参数 //
之所以这么做,是为了获得Father构造函数中的所有属性和方法 //
之所以用call,是为了修正Father内部this的指向 Father.call(this, name); }
var instance1 = new Child(“weiwei”); instance1.colors.push(‘black’);
console.log(instance1.colors); // [ ‘red’, ‘blue’, ‘green’, ‘black’ ]
console.log(instance1.name); // weiwei var instance2 = new
Child(“lily”); console.log(instance2.colors); // [ ‘red’, ‘blue’,
‘green’ ] console.log(instance2.name); // lily

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Father (name) {
  this.name = name;
  this.colors = [‘red’, ‘blue’, ‘green’];
}
function Child (name) {
  // 继承了Father,同时传递了参数
  // 之所以这么做,是为了获得Father构造函数中的所有属性和方法
  // 之所以用call,是为了修正Father内部this的指向
  Father.call(this, name);
}
var instance1 = new Child("weiwei");
instance1.colors.push(‘black’);
console.log(instance1.colors); // [ ‘red’, ‘blue’, ‘green’, ‘black’ ]
console.log(instance1.name); // weiwei
var instance2 = new Child("lily");
console.log(instance2.colors); // [ ‘red’, ‘blue’, ‘green’ ]
console.log(instance2.name); // lily

为了确保Father构造函数不会重写子类型的属性,可以在调用超类型构造函数后,再添加应该在子类型中定义的属性。

JavaScript 数组的进化

不知你是否记得我们对朋友入手的 256MB
内存的电脑羡慕得要死的日子?而今天,8GB 内存遍地都是。

与此类似,JavaScript 这门语言也进化了不少。从 V8、SpiderMonkey 到 TC39
和与日俱增的 Web 用户,巨大的努力已经使 JavaScript
成为世界级必需品。一旦有了庞大的用户基础,性能提升自然是硬需求。

实际上,现代 JavaScript 引擎是会给数组分配连续内存的 ——
如果数组是同质的(所有元素类型相同)。优秀的程序员总会保证数组同质,以便
JIT(即时编译器)能够使用 c 编译器式的计算方法读取元素。

不过,一旦你想要在某个同质数组中插入一个其他类型的元素,JIT
将解构整个数组,并按照旧有的方式重新创建。

因此,如果你的代码写得不太糟,JavaScript Array
对象在幕后依然保持着真正的数组形式,这对现代 JS 开发者来说极为重要。

此外,数组跟随 ES2015/ES6 有了更多的演进。TC39 决定引入类型化数组(Typed
Arrays),于是我们就有了 ArrayBuffer

ArrayBuffer
提供一块连续内存供我们随意操作。然而,直接操作内存还是太复杂、偏底层。于是便有了处理
ArrayBuffer 的视图(View)。目前已有一些可用视图,未来还会有更多加入。

var buffer = new ArrayBuffer(8); var view = new Int32Array(buffer);
view[0] = 100;

1
2
3
var buffer = new ArrayBuffer(8);
var view   = new Int32Array(buffer);
view[0] = 100;

了解更多关于类型化数组(Typed Arrays)的知识,请访问 MDN
文档。

高性能、高效率的类型化数组在 WebGL 之后被引入。WebGL
工作者遇到了极大的性能问题,即如何高效处理二进制数据。另外,你也可以使用
SharedArrayBuffer
在多个 Web Worker 进程之间共享数据,以提升性能。

从简单的哈希映射到现在的 SharedArrayBuffer,这相当棒吧?

发表评论

电子邮件地址不会被公开。 必填项已用*标注

标签:, ,
网站地图xml地图