loethen's blog

养猫写码 meow and code


  • Home

  • Archives

JavaScript中this用法总结

Posted on 2019-11-08

基础知识时间长了也会忘, 虽然可能用对了, 但是又不知道为什么会对? 最后陷入了自我怀疑, this 可以说是js里面的灵魂关键字, 如果不能完全掌握它的原理, 你就不能随心所欲的使用.

DOM中使用this

来, 让我们以一个例子开始:

function doSomething() {
    this.style.color = '#cc0000';
}

这里的this 指向谁? 在Javascript中 this总是指向这个函数执行时的所以者. 如果用一句话来总结this的用法, 应该就是这一句. 如果 doSomething()直接在页面中调用 ,那它的所有者就是这个页面本身, 或者window对象(或者global对象).

如果是在html元素的onclick属性中调用, 那this就指向这个HTML元素

element.onclick = doSomething;

有一个情况要特别注意:

<element onclick="doSomething()">

内联事件中的函数, 不是作为html元素的属性调用的! 它只是对函数的引用! 所以这里调用doSomethind()函数里面的this指向window, 因为它仅仅是找到doSomething然后执行它:

doSomething();

以下几种this指向html元素:

element.onclick = doSomething
element.addEventListener('click',doSomething,false)
element.onclick = function () {this.style.color = '#cc0000';}
<element onclick="this.style.color = '#cc0000';">

以下几种this指向window

element.onclick = function () {doSomething()}
<element onclick="doSomething()">

内联事件中正确用法:

<element onclick="doSomething(this)">

function doSomething(obj) {
    // this is present in the event handler and is sent to the function
    // obj now refers to the HTML element, so we can do
    obj.style.color = '#cc0000';
}

全局环境

全局环境中(任何函数外) this 指向全局对象.
在浏览器中指向 window

函数内部环境

this 取决于函数调用的方式 (先不讨论严格模式))

看下面的例子

function f1(){
    return this;
}

调用f1(), 将返回window对象, 因为直接调用f1()相当于调用window.f1() ,所以这里的 this 指向window对象

如果想要改变this的执行环境,就要用到call or apply方法.

看下面的例子

var obj = { a: 'chestnut'}
var a = 'pinapple'

function hold_what(arg){
    return this.a;
}

hold_what(); // return 'pinapple'
hold_what.call(obj);  // return 'chestnut'
hold_what.apply(obj); // return 'chestnut'

注意的是call或者apply的第一个参数,如果不是一个对象,js会尝试将其转换成对象.

bind方法调用this

调用f.bind(obj)会创建一个新的函数, 但是函数体内的this将永久的被绑定到obj. 无论这个函数在哪里被调用

function f(){
    return this.a;
}

var g = f.bind({a:"azerty"});
console.log(g()); // azerty

var h = g.bind({a:'yoo'}); // bind只生效一次!
console.log(h()); // azerty

var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty

箭头函数内调用this

// 创建一个含有bar方法的obj对象,
// bar返回一个函数,
// 这个函数返回this,
// 这个返回的函数是以箭头函数创建的,
// 所以它的this被永久绑定到了它外层函数的this。
// bar的值可以在调用中设置,这反过来又设置了返回函数的值。
var obj = {
    bar: function() {
        var x = (() => this);
        return x;
    }
};



//作为对比,创建一个obj2
var obj2 = {
    bar: function() {
        var x = function(){ return this };
        return x;
    }
}

var fn = obj.bar();
var fn2 = obj2.bar();

console.log(fn()===obj);     //结果1:  返回true
console.log(fn2()===obj2);   //结果2:  返回false
console.log(fn2()===window); //结果3:  返回true

// 但是注意,如果你只是引用obj的方法,
// 而没有调用它
var fn3 = obj.bar;
// 那么调用箭头函数后,this指向window,因为它从 bar 继承了this。
console.log(fn3()() == window); // true

看结果,体会箭头函数和普通函数的区别. 特别注意这行注释 所以它的this被永久绑定到了它外层函数的this。

作为对象的方法调用this

函数作为对象里的方法调用时, this指向调用该函数的对象.

var o = {
    prop: 37,
    f: function() {
        return this.prop;
    }
};

console.log(o.f()); // logs 37

看下面的例子

var o = {prop: 37};

function independent() {
    return this.prop;
}

o.f = independent;

console.log(o.f()); // logs 37

o.b = {g: independent};
console.log(o.b.g()); // undefined

这里注意 o.b的例子中,this指向o.b.与她的对象o没什么关系.

原型链中的this

var o = {
    f: function() { 
        return this.a + this.b; 
    }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5

如果该方法存在于一个对象的原型链上,那么this指向的是调用这个方法的对象,就像该方法在对象上一样。原型链这里很有意思. 虽然p没有f方法, 但是在它的原型链中找到了, 但是f是作为p的方法调用的, 所以它的this指向p. 多读两遍这句话, 真的很有意思.

构造函数中的this

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

如果构造函数中手动返回了对象, 那this绑定的默认对象就会被丢弃.

function C2(){
    this.a = 37;
    return {a:38};
}

o = new C2();
console.log(o.a); // logs 38

总结一下

JavaScript是面向对象的/

参考 ppk the this keyword MDN this

用Puppeteer破解极验滑动拼图验证码

Posted on 2019-08-15

puppeteer是什么:

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

你看puppeteer这个单词是木偶戏的意思, 你再看它的logo, 可以说非常传神了. 简单的说,就是让你可以通过代码模拟人在浏览器的操作.

具体的DEMO看下面的链接. 模仿了什么值得买网页端的登陆后签到.

过程中难点就是怎么通过代码模拟极验的滑动验证码, 需要注意的就是鼠标的滑动轨迹不要匀速滑动.

完整的代码看这里

Javascript函数防抖和节流(debounce and throttle)

Posted on 2019-05-28

理解线程和进程的关系

Posted on 2019-03-20

探索并发的世界,一次一步。

得利于硬件的进步和更智能的操作系统,现代计算机都有能力同时执行多个操作。就执行和响应速度而言,计算机的这个特性使你的程序运行的更快。

编写利用这种能力的软件,是如此的吸引人。但是棘手的是,它需要你了解你的计算机在执行的时候,到底发生了什么。在这篇文章中,我将试着揭开线程的面纱,线程是操作系统提供的工具之一,用来执行这种有”魔力“的操作,让我们开始吧!

进程和线程:用正确的方法命名

现代操作系统可以同时运行多个程序。这就是为什么你能在浏览器里看这篇文章的同时,又能用音乐播放器听音乐。我们知道每个被执行的程序被称为一个进程。操作系统可以利用底层硬件,和一些软件的技巧来使每个进程独立于其他进程运行。无论是哪种方式,最终的结果就是你感觉你的程序都在同时运行。

运行进程不是操作系统同时执行数个操作的唯一方式,每个进程的内部,可以同时运行多个子任务,这些子任务称为线程。你可以想象一下,一个线程就是一个进程的切片。在进程开始运行的时候,内部至少触发一个线程,这个线程称为主线程。然后根据程序或者程序员的需要,可以额外增加或结束其他线程。多线程就是在一个进程内部运行多个线程。

举个栗子,就像你的音乐播放器就是多线程,一个主线程用来渲染界面,另一个线程用来播放音乐或其他等等操作。。

你可以把操作系统当成一个容器,这个容器里面盛满了进程,每个进程里面又含有很多线程。这篇文章仅仅关注线程,但是这整个话题是如此的吸引人,以后值得更深入的分析。

进程和线程的区别

每个进程都有自己的内存块,这个是操作系统分配的。默认这个内存不能和其他进程共享,你的浏览器没有权限访问你音乐播放器的内存,反之亦然。同样的,如果你打开浏览器两次,那是两个独立的进程,有不同的内存区域。所以默认的,两个进程没办法分享数据,除非他们执行一个高级的技巧–IPC(inter-process communication)

跟进程不一样, 线程共享操作系统分配给他们所属进程的内存块。你音乐播放器的界面数据是可以容易的被音频引擎访问的,反之亦然。最重要的是,比起进程,线程是很轻量级的,他们消耗很少的资源,创建起来很快,这就是为什么它们又被称为轻量级的进程

Green threads, of fibers

当前提到的线程,都是操作系统层面的事情:一个进程需要触发线程,只能与操作系统通信。但是不是每一个平台都原生的支持线程。所以绿色线程也被称为纤维,就是这种情况下,多线程的一个替代品。比如一个虚拟机就可能实行绿色线程,因为底层操作系统不支持原生的线程。

在运行层面上,创建和管理绿色线程是非常快的,因为它的实现绕过了操作系统。但是也有缺点,我们将在下一篇文章中讨论。

绿色线程名字来源与Sun公司的绿色团队(Green Team),他们是90年代原生Java线程库的设计师。现在,Java不再使用绿色线程了,他们在2000年又转到原生线程上了。其他一些编程语言– Go, Haskell, Rust等都实现了自己的绿色线程。

线程能用来做什么

为什么进程需要雇佣多个线程?就像我前面提到的,同时做很多事情,能够极大的提高做事情的效率。就说你在影片编辑器里渲染一部电影,这个编辑器应该足够智能的在多个线程间传递渲染操作,每个线程都处理整个影片中的一段。所以当只有一个线程可以用的时候,可能要花一个小时,2个线程的话30分钟,4个线程的话,15分钟。

真的这么简单?还有3个重要的点需要考虑:

  • 不是每个程序都需要使用多线程,如果你的应用是处理有序(串联)的操作,或者经常需要等待用户的响应,多线程可能不是那么适合。
  • 别只是为了使你的程序运行的更快,而抛出更多的线程。每个子任务都要小心的考虑和设计它们的并行操作。
  • 线程在真正的并发下并不不能100%保证正确的处理它们的操作。这取决于底层的硬件。

最后一条是很关键的。如果你的计算机不支持多线程的同时操作,操作系统必须模拟它。让我们用1分钟的时间看看是如何做的,假设并发(concurrency)是我们的一种感知,就是有多个任务在同时处理,而真正的并行(parallelism)是同时处理的任务。

是什么使并发和并行成为可能

电脑里的CPU做了运行程序中最艰苦的工作,它包含以下几个部分,最主要的称为核心,这是计算真正执行的地方。一个核心同一时间只能做一个操作。

这当然是一个主要的缺点。基于这个原因,操作系统又开发了高级的技术,使用户有能力同时运行多进程(或线程),尤其是在图形环境中。
即使是单核的机器。其中最重要的一个技术称为抢先式多任务处理(preemptive multitasking),就是打断正在执行的任务,转换到另一个任务上,过一会再转回来,

所以,当你的计算机只有单核的时候,操作系统的部分任务就是传播这个单核的算力,跨越多个进程或线程。循环执行他们中的一个到另一个。这个操作会给你一种错觉,让你感觉好像多个程序在同时执行,或者一个程序同时做了多件事情。我们已经讲到了并发,但真正的并行– 能够同时运行多个进程,还没讲到。

如今现代的CPU都有多个核心,每一个都可以同时执行一个独立的操作,这就意味着真正的并发成为可能。比如我的inter core i7 有4个核心,它能同时处理4个进程或线程。

操作系统能够探测到CPU的数量,然后给它们分配进程或者线程。线程可能分配给操作系统喜欢的任何核心,这种类型的调度对你的程序来说是完全透明的,另外,如果所有核心都在忙的话,抢先式多任务处理就应该起作用了。这就给你了机会,来运行比你计算机真正核心数多的进程和线程。

单核心运行多线程程序,真的有意义吗?

真正的并行在单核机器上是不可能实现的。如果你的程序能够受益于多线程,那写一个多线程的程序还是有意义的。当程序使用多线程的情况下,抢先式多任务处理能够保证你的应用正常运行,即使其中有个线程在执行很慢,或是闭塞的任务。

举个栗子,你在开发一个桌面应用,从一个很慢的硬盘里面读数据,如果你写的程序是单线程的,整个app会冻结,直到硬盘的操作完成,在等待磁盘唤醒时浪费了分配给唯一线程的CPU功率,当然,操作系统除此之外还运行许多其他进程,但你的应用程序不会有任何进展,只会停在那里。

让我们重新考虑一下你的应用程序,如果改成多线程呢? 线程A用来访问硬盘,同时线程B负责主要的界面。当线程A因为硬盘原因被卡住的时候,线程B仍然能够运行主要的用户界面,保持你的程序正常响应。当你有两个线程的时候,操作系统可以让CPU资源在它们之前转换,而不是卡在慢的那个上面。

线程更多,问题更多

我们知道,线程共享它们父进程的内存块,这就使得多个线程可以很容易的互相交换数据,在同一个应用内。举个例子,一个电影编辑器可能占用含有视频时间轴的一大部分共享内存,而这里共享的内存又可以被他的多个工作线程读取,这里的线程被设计用来渲染影片,并导出到硬盘文件。它们只需要一个到内存区块的指针,用来读取然后输出渲染的帧到硬盘。

只要两个或多个线程都从相同的内存位置读取数据,事情就能顺利的进行。But,注意这个but,让至少一个线程往内存里面写数据,而同时多个其他线程在读数据,问题就来了,这里有两个问题:

  • 数据竞赛 – 让一个线程在执行写操作,而一个现在在执行读操作,如果这时候,写还没完成,那读肯定会得到一个坏的数据。
  • 竞赛条件 – 读操作应该在写操作完成之后。但是如果相反的情况会发生什么?比数据竞赛更微妙的是,当两个或多个线程以不可预期的顺序做他们的工作,而实际上这些操作应该以特定的顺序执行,你的程序就会触发一个竞赛条件。

线程安全的概念

一段代码如果能正常工作,被称之为线程安全,也就是没有数据竞赛或者竞赛条件,尽管许多线程在同步执行。你可能已经注意到一些编程库声称自己是线程安全的:如果你在写多线程的程序,你希望确保任何其他的第三方函数能够跨线程正确的调用,而不触发并发问题。

数据竞赛的根本原因

我们知道CPU核心同一时间只能执行一条机器指令,这种指令被称为原子操作,因为它不能再分解:不能再分解成更小的操作。

这种不能分割的特性使得原子操作自带线程安全。当一个原子在执行写操作的时候,其他线程是不可能读完成一半的数据的。反过来,当一个线程在执行一个原子读操作的时候,肯定是读的当时完整的数据。对一个线程来说,是不可能遗漏原子操作的,所以不可能发生数据竞赛。

坏消息是,绝大部分的操作都不是原子操作。就是一个小小的赋值语句 x = 1,在一些硬件上都可能是多个原子机器指令的组合。使得赋值本身变成非原子操作。所以在赋值的时候,如果一个线程在读x的值,那就会触发数据竞赛。

竞赛条件的根本原因

抢先式多任务处理使操作系统能够完成的控制进程管理。通过高级的调度算法,它能开始,停止或者暂停线程。你作为一个程序员,是不能控制执行的时间和顺序的。实际上,像下面这样的代码是没有保护的

writer_thread.start()
reader_thread.start()

这段代码将以特定的顺序,开始两个进程。运行几次你将会注意到,每次运行的行为都不一样:有时writer线程先开始,有时候reader线程先开始,如果你的程序需要writer线程总是先执行,那你肯定会触发竞赛条件。

这种行为称为 非确定性 :你不能预测到每次执行的顺序。在debug由竞赛条件引起的错误的时候,会让人很恼火,因为你不能每次都重现这个错误。

教会线程相处: 并发控制

数据竞赛和竞赛条件都是现实中会遇到的问题。容纳两个或多个并发线程的技术称为并发控制,操作系统和编程语言提供了好几种方案来处理它。

  • 同步 – 确保资源同一时间只能被一个线程使用。同步是将代码的特定部分标记为“受保护”,以便两个或多个并发线程不会同时执行它,从而搞砸了你的共享数据;

  • 原子操作 – 一堆的非原子操作(像前面提到的赋值)通过操作系统提供的特殊指令,可以被转化为原子操作。用这种方法,共享的数据总会保持有效的状态,不管其他的线程以何种方式访问它。

  • 不可变数据 – 共享的数据标记为不可变状态。线程只允许读,从根本上解决问题。我们知道,如果是只读的话,访问相同的内存地址里面的数据是安全的。这也是函数编程里面的主要哲学。

翻译自A gentle introduction to multithreading

单词

“under the hood”

fiber: In computer science, a fiber is a particularly lightweight thread of execution.
https://en.wikipedia.org/wiki/Fiber_(computer_science)

计算机在识别图片领域是怎么做到这么厉害的

Posted on 2019-03-12

本文翻译自How computers got shockingly good at recognizing images

2012年一篇里程碑式的论文改变了软件识别图片的方式。

现在,我可以打开谷歌相册,输入”海滩“,将会看到大量的海滩照片,这些都是我过去十年去过的地方。我从来没有给这些照片打过标签,取而代之的是,谷歌确认这些照片中的海滩,是通过这些照片里面的内容本身。这个看起来好像挺普通的功能,是运用了一项技术,叫做深度卷积神经网络。这个技术能让软件以一种复杂的方法”理解”这张图片,在这之前的技术是不可能做到的。

最近几年,研究人员发现随着他们建立深度网络,并使用大量的数据训练她们,软件的识别准确性越来越高了。这也创造了对计算机能力几乎无法满足的胃口,提升了那些GPU厂商的财富,例如Nvidia, AMD。谷歌几年以前,就开发了自己的定制神经网络芯片,其他公司也积极的跟进。

举个例子,在特斯拉,公司请到了深度学习专家Andrej Karpathy负责公司的自动驾驶项目。特斯拉正在开发自己的芯片,来为下一代的自动驾驶加速神经网络运算。另一个例子, 在苹果公司,A11和A12芯片在最近的iPhone的核心中都包含了“神经引擎”,用来加速神经网络运算。以此提供更好的图片和声音识别应用。

这篇文章中我与之交谈的专家,将当期深度学习热潮追溯到一篇特定的论文,绰号AlexNet,论文的主要作者Alex Krizhevsky.

“在我的印象中,2012年是里程碑式的一年,当AlexNet的论文出来以后”,机器学习的专家Sean Gerrish说。

回到2012年,在机器学习领域,深度神经网络其实是某种意义上的“回水”(我的理解是之前也有人研究,但是效果不理想),但是当时Krizhevsky和他的多伦多大学同事在一个图片识别竞赛上,高调的提交了一个作品,显著的比之前所有开发的都要精确。几乎隔夜,深度神经网络变成了图片领域的前沿技术。其他的研究人员利用这个技术很快的证明了图像识别准确性的进一步飞跃。

在这篇文章中,我们将深入的挖掘深度学习,我将要解释什么是神经网络,怎样训练他们,为什么他们需要如此多的计算能力。另外,我讲解释为什么一个特殊的神经网络类型–深度,卷积网络–在识别图片方面如此的优秀。不用担心,下面会有很多图片。

单神经元的简单例子

可能你对“神经网络”这个词,会感到有一丝的困惑。让我们以一个简单的例子开始吧。假设你想要用神经网络来决定一辆车是否能够通过红绿灯。一个单神经元的神经网络应该能完成这个任务。

神经元取每次的输入(1 表示打开,0 表示关闭),和它关联的权重相乘,并将所有加权值相加。然后神经元再加上偏差值,得到的结果决定神经元“激活”的阈值。在这种情况下,如果输出值是正的,我们假设神经元已经”fired”(不知道怎么翻,有点像开火,点火,激活之类的意思吧),否则我们不会。这个神经元等于一下的不等式“绿 - 红 - 0.5 > 0”,如果这个不等式结果为true,意味着绿灯亮了,红灯灭了,车可以通过。

在真正的神经网络里面,人工的神经元要经过额外的一个步骤。在取加权值之和,再加上偏差值以后,要调用一个非线性的激活函数,一个比较流行的选择是sigmoid function, 一个S-shaped function总是产生(0,1)之间的值.

这个使用的激活函数不会改变我们这个简单的红绿灯模型的结果(除了我们需要使用0.5而不是0的阈值)。但是激活函数的非线性对于使神经网络能够模拟更复杂的函数至关重要,如果没有非线性激活函数,每个神经网络,不管多复杂,都可以简化其输入的线性组合。然而,一个线性函数是没法模拟复杂的真实世界的现象。非线性激活函数使得神经网络能够近似任何数学函数成为可能。

一个神经网络的例子

有很多的方法来近似函数,当然,神经网络的特殊之处在于我们知道如何使用一些微积分知识,大量的数据,还有大量的计算
能力来训练它们。我们可以构建一个通用的神经网络,而不是让一个人类程序员为了特定的任务而设计一个神经网络。通过观察大量带标签的示例,然后修改神经网络,以使它能够为已标记的示例产生尽可能多正确的标签。最后希望的结果是这个网络能够为哪些没在训练集里面的示例数据归纳,产生正确的标签。

到达这一点的过程在AlexNet之前就开始了。 1986年,三位研究人员发表了一篇关于反向传播的具有里程碑意义的论文,这种技术有助于使其在数学上易于训练复杂的神经网络。

为了有一个直观的了解,让我们看一下Michael Nielsen在他出色的在线深度学习教科书中展示的简单的神经网络。这个神经网络的目标是,取一个28*28像素的图片来表示一个手写的数字,正确的认出这个数字是0,1,2等等。

每张图片有28*28=784个输入值,每个值在0和1之间,代表像素的明暗程度。Nielsen构建了一个神经网络,像下面这样:

在这张图片中,中间和右边的每一个圆圈代表一个神经元,就像我们在上面章节看到的那样。每个神经元对其输入进行加权平均,添加偏差值,然后应用激活函数。注意左边的圆圈不是代表神经元,这些圆圈代表这个网络的输入值。虽然图像仅显示8个输入圆,但实际上有784个输入 - 输入图像中的每个像素一个。

右边的10个神经元表示为不同的数字“亮灯”,当输入的图片是手写的0,第一个神经元会“亮灯”,以此类推。

每个神经元从它之前层级的每个神经元获取输入,所以中间层中15个神经元中每个神经元都有784个输入值,这15个神经元中的每一个都有相对应784个输入值的权重参数,这就意味着这一层有单独的15x784=11760个权重参数。相似的,输出层包含10个神经元,每个神经元都从中间层的15个神经元中获取输入值,增加另外的15x10=150个权重参数,最重要的是,这个网络还有25个偏差值-这25个神经元每个都有。

训练这个神经网络

训练的目标是调整这11935个参数,最大化的使那个代表正确数字的,输出层的神经元亮灯。我们可以用那个很出名的数据集MNIST,它提供了60000个打了标签的28*28像素的图片:

This image shows 160 of the 60,000 images in the MNIST dataset.

Nielsen展示了如何训练这个网络,而仅仅使用了74行常规的Python代码–不需要特别的机器学习库。训练以选择这11935个参数和偏差开始。然后软件会查看示例图片,为每张图片完成以下两个处理步骤:

* 前馈步骤 给定输入图片和网络当前的参数,计算网络的输出值
* 反向传播步骤 计算结果偏离正确的输出值有多少,然后修改网络的参数,以略微提升在特定图片上的表现。

下面举个栗子,假设网络显示了这张图片

如果网络已经校准了,那网络中输出层代表”7“的神经元应该趋近于1,其他的应该趋近于0。但是出乎意料的是,当展示这张图片的时候,代表”0“的神经元趋近于0.8,这太高了!这个训练算法应该调整”0“的输入权重,使得下次能够让它趋于0。

为此,反向传播算法计算每个输入权重参数的误差梯度。作为一个衡量标准,对于给定的一个输入权重值的变化,所引起的输出值的误差变化的一个衡量标准,算法使用这个梯度来决定需要改变每个输入权重值的程度。梯度越大,需要改变的参数越多。

换句话说,训练程序”教”输出层的神经元,不要把注意力放在那些朝错误方向的输入值上(这里指中间层的神经元),而是多关注那些能够朝正确的输入值方向上。

该算法针对每个其他输出层神经元重复该步骤。它减少了“1”,“2”,“3”,“4”,“5”,“6”,“8”和“9”神经元(但不是“7”神经元)的输入权重将这些输出神经元的值向下推

plus: 文章没翻译完,一是太长了,二是这里的数学知识不太了解,等我入门机器学习以后,再继续翻译吧。有英文基础的建议看原文,虽然可能有些数学原理看不懂,但是还是能大概理解什么是机器学习。

Udacity免费课程
Google tensorflow 免费课程

原文地址 How computers got shockingly good at recognizing images

宜家一张画背后的故事

Posted on 2018-12-19

今天网上看到一个故事很有意思,我在一个博客上看到的,之后我去看了原文和视频。以下内容根据原文内容翻译和加工。

Tom买了一张宜家的装饰画,就是下面图片这张,画面是阿姆斯特丹的街头,然后Tom买回去以后,很多朋友问他为什么会选择这个看起来很“平庸”的照片挂在自己的卧室,然后呢,Tom决定探究一下,宜家是怎么选择的这张照片。要知道这个装饰画在宜家已经卖出去了427000份,是的,你没有看错,就是这么多。

事情过程大概是这样的。

Tom在宜家的网站上找到了这张照片的拍摄者名字叫费尔南多,然后呢,打算联系他,网上一搜,照片作者在2004年的印度海啸中失踪了,反正没找到,辗转联系到了照片作者的哥哥,知道了费尔南多失踪时候已经是纽约最好的摄影师之一了。当时为O Magazine, Vogue, Architectural Digest等等杂志工作,也为Tommy Hilfiger拍摄。通过费尔南多的哥哥,又联系到了费尔南多当时的男友Nate Berkus,Berkus当时和费尔南多一起在印度度假,但是他奇迹的活了下来。通过Berkus,又联系到了费尔南多的前男友Afkami,来自纽约城的一个建筑师。Afkami说,这张照片拍摄于1999年的三月,当时他俩吵架了,费尔南多一个人在街头游荡,拍了这个照片是为了向Afkami道歉。在费尔南多死后,他拍摄的一些没有用于商业目的的照片,被传到了一个图片网站,然后有一天宜家联系费尔南多的哥哥,想用这张照片。最终宜家出了这个100*120cm的装饰画,在被宜家带到世界各地的同时,很多人会联系费尔南多的哥哥,想知道这张照片叫什么名字,很多人去阿姆斯特丹的这个地方,都是因为这张照片。最终买装饰画的这个人联系到了宜家负责选照片的负责人(干这活已经10来年了),然后问他为什么选择这张照片,这个负责人说,选择的标准第一就是便宜,如果不便宜宁愿会放弃,这张照片非常非常便宜,这就是他们选择的原因。

是不是没想到

Tom: “他永远不可能知道他的照片现在被挂在超过427000个房间“
Ikea: ”是的,这是比较悲伤的一面,但是他的家人会非常非常为他自豪“

下次去宜家我会好好看看这幅画。




现在它被挂在全世界各个地方。

有兴趣的可以自己看看原文 https://petapixel.com/2018/11/20/the-story-behind-that-ikea-photo-of-amsterdam/

plus: 翻译文章不容易啊,这篇文章前前后后大概花了2个小时吧。

JavaScript Event Loop

Posted on 2018-12-14

JavaScript中的事件循环

JS中的事件循环你可能你知道,也可能你不知道,也可能你不知道自己到底知不知道,
当然了,不管你知不知道,你每天写的JS代码都离不开这个概念,不管是客户端,还是Node.

我们先看下面的例子

console.log('Hi')
setTImeout(function(){
    console.log('there')
},5000)
console.log('JSConfEU')

你觉得执行的顺序是怎么样的?

JavaScript是单线程的编程语言,意思就是同一时间只能做一件事情。老司机和新手司机可能都知道输出顺序,但是你知道为什么是这样吗?再举一个例子,想一下下面的执行顺序是什么呢?

console.log('Hi')
setTImeout(function(){
    console.log('there')
},5000) 
console.log('JSConfEU')

先看一下这张图片。里面有几个概念,Heap, Stack, Queue.

我们先把堆放一边,看看什么是栈。

什么是Stack(栈)?

每当有一个函数调用,就会将其压入栈顶。在调用结束的时候再将其从栈顶移出。栈的特性是先进后出,后进先出。栈是有序的
想一下上面的代码。执行结果应该都是

Hi
JSConfEU
there

到底是怎么做到的呢?再看一下下图。通过以下视频应该就能理解了。

什么是Queue(队列)

这里的列队指的是task queue(任务队列),队列也是有序的,先进先出,在当前栈空的情况下,队列中的函数会按加入的先后顺序依次被推到栈中执行。

最后说说什么是Heap(堆)

简单的说一下什么是堆,因为这个概念看了一些网上的介绍和维基百科,还是云里雾里的,直到看到一个视频,才对堆和栈有个宏观的了解,下面我简单的描述一下。
讲到堆,就必须要拿栈来对比。先看下面这张图

栈

用书、矿泉水瓶、包等等物品排队的现场,这些物品排的队可以代表栈,旁边随便站的那些无序的人就是堆。栈里面只能存一些primitives value.比如var a = 1, b = 2这样的,因为栈是非常快的,需要的是处理速度,而堆相对很慢,用来存储一些复杂的对象,然后栈里面存在一个pointer指向堆里面的对象。用刚刚排队的例子来看,这些书,或者瓶子就是pointer(指针),当排队排到它的时候,必须要物品的主人过去。

说到这里,垃圾回收也很好理解了, 垃圾回收是一个释放计算机内存的过程。当对象0引用的时候,就可以被回收了。还拿刚刚的图片举例,如果某个人办完事了,那他不会留在这里,还会把他的物品带走。如果某个人站在那里,但是他没放物品在那里排队,那他也会被回收。属于无效资源,还浪费空调。。

墙裂推荐结尾附录的这个视频Stack Versus Heap,简单明了。

差不多介绍完了,主要是对自己学习的一个总结。

本篇内容参考
Philip Roberts: What the heck is the event loop anyway?
MDN EventLoop
Stack Versus Heap

puppeteer能做点啥?

Posted on 2018-12-13

JavaScript 继承和原型链

Posted on 2018-12-13

ELO rating system匹配算法

Posted on 2018-10-16
12
loethen

loethen

frontend javascript 前端 js cat

18 posts
6 tags
© 2019 loethen
Powered by Hexo
|
Theme — NexT.Muse v5.1.4