Hi. I’m Hadeser.

Here are some thoughts of mine.

理解Javascript函数调用和函数内部This引用

一直以来, 我对Javascript函数调用都很困惑, 不仅仅是函数调用, 很多人都抱怨函数内部的this引用也混乱。

在我看来, 只要是明白了函数的调用的原始本质,就会明白很多, 其实他们方式的函数调用只不过是在其原始调用的基础上增加一些语法糖而已。其实这也是ECMAScript所要描述的那样, 在这篇文章的重点就是讲这些。

函数调用本质

首先,我们看看函数调用的本质,就是一个函数调用 call 方法, 调用 call 方法是相对于直接通过括号()调用

  1. 把第二个参数到最后一个参数当做一个参数列表(argList)。

  2. 第一个参数就是函数内部This的值(thisValue).

  3. 调用函数并且把thisValue设置到函数内部this, argList当做函数的参数列表。

例如:

1
2
3
4
5
function hello(thing) {
      console.log(this + " says hello " + thing);
    }

    hello.call("Yehuda", "world") //=> Yehuda says hello world

在console运行代码,你就会发现this值被设置成了”Yehuda”, 参数thing设置成立了”world”, 这就是javascript函数原始的调用, 然后你再看看其他的调用方式, 只是在这个的基础上加上语法糖而已。the ES5 spec

简单函数调用

很显然,每次调用函数都通过call 来调用会很烦, javascript允许我们直接使用()语法来调用函数, 当我们这样调用时, 实际上就像应用了下面的方式。

1
2
3
4
5
6
7
8
9
function hello(thing) {
        console.log("Hello " + thing);
    }

    // this:
    hello("world")

    // desugars to:
    hello.call(window, "world");

当上面的代码在ECMAscript5 的严格模式下, 行为会不一样。

1
2
3
4
5
// this:
    hello("world")

    // desugars to:
    hello.call(undefined, "world");

上面的版本可以这样表示: 一个函数调用 fn(...args) 就像 fn.call(window [ES5-strict: undefined], ...args)

注意函数表达式直接调用也是应用同样的道理(function() {})()(function() {}).call(window [ES5-strict: undefined)是一样的。

对象方法函数

另一种比较常用的函数调用是作为对象方法的方式。(person.hello()), 像下面这种方式:

1
2
3
4
5
6
7
8
9
10
11
12
var person = {
      name: "Brendan Eich",
      hello: function(thing) {
        console.log(this + " says hello " + thing);
      }
    }

    // this:
    person.hello("world")

    // desugars to this:
    person.hello.call(person, "world");

在这个例子中hello是作为person对象的一个方法, 而上一个示例中hello是作为独立的方法。 如果在程序执行过程, 动态指定方法到一个对象里,情况是怎样的呢?

1
2
3
4
5
6
7
8
9
10
function hello(thing) {
      console.log(this + " says hello " + thing);
    }

    person = { name: "Brendan Eich" }
    person.hello = hello;

    person.hello("world") // still desugars to person.hello.call(person, "world")

    hello("world") // "[object DOMWindow]world"

注意, 函数里面的this的值并不是总固定的,它的值总是依赖于当时函数调用者。

使用函数原型的 bind 方法

因为有时我们想函数里面this能保持不变。因此,人们在很久以前就使用闭包的方式来保持函数里面this的值不变。

1
2
3
4
5
6
7
8
9
10
var person = {
      name: "Brendan Eich",
      hello: function(thing) {
        console.log(this.name + " says hello " + thing);
      }
    }

    var boundHello = function(thing) { return person.hello.call(person, thing); }

    boundHello("world");

尽管boundHello的调用方式转换为 boundHello.call(window, "world"), 但是在方法里面,我们通过闭包引用person,直接调用指定this值, 最终还是达到了我们的效果。

我们还可以优化下上面的代码, 使的更通用。

1
2
3
4
5
6
7
8
var bind = function(func, thisValue) {
      return function() {
        return func.apply(thisValue, arguments);
      }
    }

    var boundHello = bind(person.hello, person);
    boundHello("world") // "Brendan Eich says hello world"

为了明白上面的代码, 你需要了解两点: 首先arguments是一个类似数组的对象,但并不是一个真正的数组, 代表代码函数执行时,传递过来的参数列表。 其次是apply 方法其实它和call方法十分的相似,唯一的区别就是,它只接收一个类数组对象的参数并不像call那样一个个参数传递。

这里bind 方法只是简单的返回一个函数。当我们返回的函数被调用时,原始的函数被调用,并且原始的thisValue设置成this值。 arguments 当作参数列表。

由于这个方式普遍应用, ES5实现了这种行为, 并在Function原型上引进了这个bind方法

1
2
var boundHello = person.hello.bind(person);
    boundHello("world") // "Brendan Eich says hello world"

当你需要传递一个函数作为回调函数时,就特别有用。

1
2
3
4
5
6
7
8
var person = {
        name: "Alex Russell",
          hello: function() { console.log(this.name + " says hello world"); }
    }

   $("#some-div").click(person.hello.bind(person));

    // when the div is clicked, "Alex Russell says hello world" is printed

英文原文


Understanding-Meteor-Internals

Meteor Interanls: Meteor是一个服务器也是一个客户端

Meteor程序,看上去就像典型的web程序一样,由浏览器,代理服务器,路由,和其他网络组件构成。事实上Meteor由两个主要的部分构成。 一部分运行在服务端,另一部分运行在客户端。 这两部分相互通信的方式就像现代Web程序一样(例如: Gmail,Trello)。

internal

Meteor 的这种方式, 使得开发人员无需再担心客户端与服务端之间复杂的通信, 只关注自己的业务逻辑。

Meteor 处理3种不同类型的请求

在Meteor内部,Meteor能够处理下面3种类型的请求:

  • 静态文件
  • DDP 消息
  • HTTP 请求

静态文件

在项目的的public 目录下得所有图片和其他资源文件都当做静态文件处理。当Meteor服务启动的时候,就能自动处理这些文件了。

DDP 消息

Meteor 程序客户端与服务端的通信就基于 DDP 协议的。客户端的所有数据订阅,Method 方法调用,Mongo数据库的操作等等这些都是通过DDP消息实现的,DDP是一个轻量级的协议。 通过这个ddp-analyzer这个工具,就可以看到DDP消息里面具体怎样操作的。

HTTP请求

虽然Meteor的官方文档并没有说明, 但是Meteor能够像其他应用程序一样处理Http请求的.例如: 通过Http请求实现文件上传。具体的你可以通过 OverflowStack 看看怎样手动处理Http请求。

Meteor 包含两种类型的服务器

虽然Meteor只运行在一个端口,但内部其实是两个独立的服务器工作的。

  • HTTP 服务器
  • DDP 服务器

internal

HTTP 服务器

HTTP服务器主要用来提供静态文件和HTTP请求,内部是使用Node.js的 connect 模块

DDP 服务器

DDP服务器处理所有的数据定义请求,MongoDB操作,Meteor 的Methods. Meteor 使用的是 SockJs作为传输协议。实际上,Meteor就是基于SockJs定制的。 后面的文章会详细介绍如何扩展这两种服务器。

Meteor and MongoDB

Meteor 是建立在MongoDB之上的,并且完全依赖与它,至少到目前为止是的, 可能在不远的将来会改变。由于MongoDb并不是一个实时的数据库。但Meteor却是实时的。Meteor使用下面的两种技术是之成为了可能。

  1. Polling MongoDB every ~10 secs

  2. Using MongoDB oplog

轮训的代价是非常大的, 因此Meteor提供另一种配置(Oplog). 但因此需要跟多的配置, 并且在一些MongoDB托管服务中并不可用。后面的章节我会更加详细的讨论这个主题。


Understanding Meteor Publications & Subscriptions

Meteor 处理应用数据的方式是这个框架最有价值的地方,也是使用它最困难的地方之一,尤其在刚开始时候。

因此造成了很多的误解.例如,它的应用是不安全的,也不能处理大数据.

今天,我花点时间来澄清这些误解,介绍Meteor的 PublicationsSubscriptions

以前的方式


开始前, 我们回顾下2011年之前Meteor还没有出现,你创建了一个简单的Rails 应用程序.当用户访问你的站点,浏览器发送一个请求给应用程序,其他的操作就全部在服务端了。

应用程序的第一个工作就是找到用户所需的数据。这可能是是12条搜索记录; 或者小明的个人信息;或者是小Q的最新动态等等

你可简单的认为就是书店店员从书架上找到你想要的书。

Mou icon

  想象下这个就是一个大的数据库

一旦拿到你想要的数据,应用程序第二个步骤就是把这些数据转换成我们熟悉的HTML(或者是json数据结构)

以书店作比喻,就像店员把书拿出来,然后包装起来放到一个漂亮的袋子里面,这就是经典的MVC模式中的视图部分。

最后应用程序把html代码发送给到浏览器,然后就悠然自得的等待下一个请求。

Meteor 的方式


Meteor 最关键的创新在于不像Rails应用程序只在服务端运行。Meteor应用还包括客户端组件, 并运行在客户端(用户浏览器).   就像是一个商店职员不但帮你找到了你要的书,还和你回家晚上陪你一起阅读(听起来是不是有点毛骨悚然)。

就是这种架构使Meteor做了很多很酷的东西,其中最主要的就是Meteor所谓的database everywhere,简单来说就是Meteor会拷贝你数据库中的一个子集复制到客户端.

data set

  Pushing a subset of the database to the client.

其实这有两层重大的含义,首先不是发送HTML code到客户端, Meteor应用程序而是发送实际的原始数据到客户端,让客户端去处理这些数据(data on the wire).

其次,你将马上访问到数据而无需等待服务器的返回。更酷的是你不要担心客户端与服务端的数据同步(latency compensation)

管理数据


我们的应用程序可能包含成千上万的数据, 有些可能是一些私密和敏感的数据。基于安全性和程序扩展性考虑, 我们不可能把整个数据库镜像都复制到客户端去。

所以如何告诉Meteor应该把哪些数据集发送到客户端去呢?

让我们忘记上面书店的比喻,看看下面的图表。

首先下面的数据存储在数据库里。想象下我们正在建立某种论坛,下面的文档就像是用户提交的帖子一样。 database

  All the posts contained in our database.

Publishing


有些帖子因为不文明而被特别标注出来,虽然这些仍然存在我们的数据库中,但是我们不应该发送到客户端去。

所以,首先告诉Meteor哪些数据我们需要发送到客户端,那些没有被标注的帖子。 database

  Excluding flagged posts.

下面是对应的代码, 运行在服务端。

1
2
3
4
// on the server
Meteor.publish('posts', function() {
    return Posts.find({flagged: false});
});

这将确保客户端不能访问已经标注出来的帖子。

确保我们Meteor应用程序安全,就是确保你发布到客户端数据的访问。

更灵活的控制

如果我们希望系统管理员能够查看被标注的帖子

很简单,假定我们定义了一个isAdmin()函数来根据用户Id检查是否有权限。像下面这样做:

1
2
3
4
5
6
7
8
// on the server
Meteor.publish('posts', function() {
    if(isAdmin(this.userId)){
        return Posts.find();
    }else{
      return Posts.find({flagged: false});
    }
});

Subscribing


即使我们想要任何未标识的帖子发送到客户端,但也不能一次把所有的帖子发过去。我们需要一种方式,只发送客户端需要的数据。 这时subscriptions派上用场了。

任何订阅的数据都会在本地客户端存一份镜像, 靠的就是 MiniMongo , 它是Meteor的Mongo客户端实现。

例如让我们想象下,目前我们正在浏览Smith的个人信息页面,只显示他相关的帖子。

database

  Subscribing to Smith’s posts will mirror them on the client.

首先我们需要在publication的地方添加一个参数

1
2
3
4
// on the server
Meteor.publish('posts', function(author) {
    return Posts.find({flagged: false, author: author});
});

我们在客户端订阅程序代码的地方应该添加一个参数。

1
2
// on the client
Meteor.subscribe('posts', 'bob-smith');

这就是怎样在客户端扩展你的Meteor程序,并不是订阅所有的数据。而是选择你当前需要的数据。这样我们可以避免因为数据库大量的数据,而造成浏览器占用大量的内存。

Autopublish 如果你已经玩过Meteor应用程序, 你可能会迷惑,我根本没有设置什么订阅和发布,为啥我的程序正常工作了。 默认新建的Meteor程序包含了 autopublish 模块。发布和订阅了所有的数据,不需要你担心了。 如果一旦你打算在生产环境部署应用了, 就应该把它移除掉。


Getstart Meteor Development

我之前就对Javascript比较感兴趣,面试我现在这家公司时,说是用Node.js 开发, 我就义无反顾的进来了。 在这边是用的基于Node.js 的一个框架 Meteor, 用了4个多月了, 现在把我用的这些心得写下来, 同时我也会翻译一些国外优秀的Meteor文章 这是第一篇。

开始

它支持不同的平台

  • 安装Meteor

$ curl https://install.meteor.com | /bin/sh

  • 新建应用

$ meteor create myapp

  • 本地运行
1
2
3
$ cd myapp
$ meteor
  => Meteor server running on: http://localhost:3000/
  • 部署到网络(免费提供服务器)

$ meteor deploy myapp.meteor.com


meteor-frontend-engineers

原文 http://davidwalsh.name/meteor-frontend-engineers

在我日常网页开发工作的同时,我也订阅了一些前端的博客。我总是感叹为啥前端工程师对JavaScript和CSS这些技术有这么深得造诣,同时他们也能够掌握这么复杂的浏览器。 然而,我也注意到很多前端开发人员似乎设定了自己,仅仅是个前端开发而已。

Front to Back

从某方面看。 随着越来越多的JavaScript框架的出现以及现代浏览器的快速发展,客户端开发的技术已经足够让你啃一辈子了.

然而事实上,却很难建立一个只与数据库打交道而不涉及后端操作的Web应用。

就目前而言,涉及到了后端操作意味着你必须学习一种新的后端语言如Ruby,Python或PHP。而新的语言都有它自己的一套模式和规范,你不得不从头开始学习.

随着Node.js作为Javascript在服务器上的的出现,这情况正在发生变化。 开发人员可以使用一种的语言编写他们的整个应用程序。

于此同时,像ParseFirebase 这种后端服务提供商的出现,使得仅仅添加几行Javascript代码就可以应用具有持久化功能成为可能。

但是今天我说得是第三种方法:Meteor 一个在客户端和服务器之间建立纽带并开始崭露头角的JavaScript框架。

我大约一年前接触到了Meteor,并迅速喜欢上了它。 我去用它建了一个流行的开放源码应用程序,并成为了一个非常成功的项目 ,并最终促使我写了一本书

Meteor 现在虽然比较年轻,如果有机会试试的话, 我认为会有很多前端工程师和设计师会很快爱上它的,

所以这篇文章送给那些正生活在浏览器世界以及还在服务器端使用JavaScript 工程师们 !

Meteor ?

让我们从基础开始:Meteor 是一种基于Node.js的一个JavaScript框架 不像大多数其他的JS框架,Meteor是在服务器和客户端同时运行,这意味着你可以用一个统一的代码库管理您的整个应用程序,并且这两个部分可以无缝地工作。

Meteor 的另一个亮点在于它的 reactivereal-time ,这意味着服务器上的任何数据更改将会立即反映在客户端上,而无需您任何额外的工作。

在实践中,这意味着您可以构建单页面的JavaScript应用,因为大多数的服务器-客户端的通信工作帮你做好了。

你已经知道它的大部分

为什么我认为Meteor是一场强大对于前端工程师而言, 因为,现有成熟的JS编译器都已经熟悉的Meteor 应用程序的代码,都是Javascript,即使他们以前从未看过Meteor的官方文档。

当然,Meteor使用JavaScript这种无处不在的语言的事实是一个很大的帮助:不同于使用Rails或者Django,你将不必学习新的编程语言。 更重要的是,Meteor 也包含一些你已经熟悉了(jQuery,underscore.js,Handlebars)的第三方类库,他们甚至都默认包含到了每一个新建的Meteor 应用(即使这样你也可以方便的替换这些使用你熟悉的)。

你不必重新学习这个新的模式,利用现有的Javascript知识, 你就可以很好的开始了。

简单设置

不像其他的开发环境, 需要安装新的语言, 模块, 框架,以及自身的版本控制。 Meteor 已经把这些东西都做好了,只需要下面这个命令可以了。只要输入:

curl https://install.meteor.com | /bin/sh

你可以打开一个终端窗口,你可以安装Meteor。

Collections 和 Reactivity

所以,如果你对JavaScript比较熟悉了以及安装也是如此简单, 那有什么真正需要学习的来建立一个Meteor 的应用程序?

对于我来说,刚学习Meteor时两个重要的概念,了解CollectionsReactivity

Collections 是MongoDB的集合等同于SQL里面的表,即存放相同数据的一个集合。 Meteor暴露了一个强大的API来发布和订阅这些Collection, 利用这个API可以让你控制哪些数据的子集,可以提供给客户端。

出于安全性以及性能方面的原因,你可能不希望把你的整个数据库数据发布到每一个客户端! 所以,建立强大的Meteor应用程序的第一步是掌握好Collection。

Meteor 另一个关键的概念是Reactivity。 简单地说,任何改变Reactivity的数据源将被立即反映到使用这个数据源的地方。

这些数据源不仅包括存储在数据库中的数据,而且包括Session变量或路由过滤器等等(后面文章会介绍)。 更重要的是, 你也可以自定义任何变量,使其具有Reactivity.

Reactivity是非常强大的也十分神奇。 所以有时候在使用时,可能会产生意想不到的影响。

所以这是存在一个学习曲线的,但掌握了这些概念之后你会很快自如的使用它来开发应用了。

一个例子

现在你大概知道Meteor是怎么一回事,让我们一起来看看Meteor一个简单的示例代码。 下面是2个 Microscope的模板(列出用户通知)的代码。 注意:如果你想知道, Microscope 是我们开发的新闻应用程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template name="notifications">
  <ul class="notification">
    {{#if notificationCount}}
      {{#each notifications}}
        {{> notification}}
      {{/each}}
    {{else}}
      <li><span>No Notifications</span></li>
    {{/if}}
  </ul>
</template>

<template name="notification">
  <li>
    <a href="{{postPagePath postId}}">
     <strong>{{commenterName}}</strong> commented on your post
    </a>
  </li>
</template>

而这里是这些模块对应的Javascript代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Template.notifications.helpers({
    notifications: function() {
     return Notifications.find({userId: Meteor.userId(), read: false});
    },
    notificationCount: function(){
     return Notifications.find({userId: Meteor.userId(), read: false}).count();
    }
  });

  Template.notification.events({
    'click a': function() {
      Notifications.update(this._id, {$set: {read: true}});
    }
  })

我敢打赌,你已经知道这段代码的含义了 第一个代码示例包含两个Handlebars的模板。 第一个遍历所有的用户通知,包括通知的每个具体项。 然后,JavaScript代码可以简单的返回那个通知列表了。 在这种情况下, return Notifications.find({userId: Meteor.userId(), read: false});仅仅意味着“返回当前用户所有未读的通知”。

当然,这一切都是被动的,这意味着你的通知列表会自动更新只要底层数据有更新!

如果您想了解更多,Meteor 模块

为什么不试试看?

现在,我不想让它听起来像学习Meteor是一个平凡的事业。 这是一个新的框架,引入了许多功能强大的概念,像每一个新的技术,它有一个学习曲线。 但Meteor提供了一个很容易上手的环境。 所以,如果现在有2,3个小时的时间,我建议你赶快来试试吧!