当前位置: 代码迷 >> JavaScript >> 深入懂得javascript的一些特性(静态作用域,this指针,闭包)
  详细解决方案

深入懂得javascript的一些特性(静态作用域,this指针,闭包)

热度:164   发布时间:2013-10-24 18:27:21.0
深入理解javascript的一些特性(静态作用域,this指针,闭包)

差不多都是这两天看书的一些总结,在这里记下来,算是个梳理。不是详细的讲解,主要记录要点。

关于javascript的静态作用域:

1. javascript的作用域完全由函数来决定,化括号不是独立的作用域。

2. javascript语言的特殊之处在于函数内部可以读取函数外部的变量。而函数外部无法读取函数内部变量。

子对象会一级一级向上寻找所有父对象变量。父对象的所有变量对子对象可见,反之不成立。

3. 静态作用域的含义是函数作用域的嵌套关系由定义时决定而不是调用时决定。又成为词法作用域,其作用域嵌套关系

在语法分析时决定,而不是运行时决定。如:

var scope = 'out'
var f1 = function(){
  console.log(scope);
};
f1();//out
var f2 = funtion(){
  var scope = 'in';
  f1();
}
f2();//out
说明函数f1在查找变量定义时,在词法分析时就已经完成,而不需要等到f1被调用的时候才开始。

4. 不通过var声明直接赋值的变量是全局变量。


关于上下文对象(this指针):

1. 上下文对象是指被调用函数所处的环境。其作用是在一个函数内部引用调用它的对象本身。

与静态作用域相对,this所指向的对象是函数调用时刻调用该函数的对象。this指针是静态作用域的

一个补充。

var obj = {
	name: 'JS',
	show: function(){
		console.log(this.name);
	}
};
obj.show();//输出 JS

var foo = {
	name: 'foo',
	show: obj.show
};
foo.show();//输出 foo
当由foo对象调用函数show的时候,this指针指向的是foo对象。

把代码中的this.name直接改成this,输出可以看到结果分别为:

{ name: 'JS', show: [Function] }
{ name: 'foo', show: [Function] }
这样就一目了然了。

为了更深刻地理解这个问题我们运行如下代码:

name = 'foo'
function f(){
	console.log(this);
};
f();
将会得到如下结果:

{ ArrayBuffer: [Function: ArrayBuffer],
  Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
  Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
  Uint8ClampedArray: { [Function: Uint8ClampedArray] BYTES_PER_ELEMENT: 1 },
  Int16Array: { [Function: Int16Array] BYTES_PER_ELEMENT: 2 },
  Uint16Array: { [Function: Uint16Array] BYTES_PER_ELEMENT: 2 },
  Int32Array: { [Function: Int32Array] BYTES_PER_ELEMENT: 4 },
  Uint32Array: { [Function: Uint32Array] BYTES_PER_ELEMENT: 4 },
  Float32Array: { [Function: Float32Array] BYTES_PER_ELEMENT: 4 },
  Float64Array: { [Function: Float64Array] BYTES_PER_ELEMENT: 8 },
  DataView: [Function: DataView],
  DTRACE_NET_SERVER_CONNECTION: [Function],
  DTRACE_NET_STREAM_END: [Function],
  DTRACE_NET_SOCKET_READ: [Function],
  DTRACE_NET_SOCKET_WRITE: [Function],
  DTRACE_HTTP_SERVER_REQUEST: [Function],
  DTRACE_HTTP_SERVER_RESPONSE: [Function],
  DTRACE_HTTP_CLIENT_REQUEST: [Function],
  DTRACE_HTTP_CLIENT_RESPONSE: [Function],
  global: [Circular],
  process: 
   { title: 'node',
     version: 'v0.10.10',
     moduleLoadList: 
      [ 'Binding evals',
        'Binding natives',
        'NativeModule events',
        'NativeModule buffer',
        'Binding buffer',
        'NativeModule assert',
        'NativeModule util',
        'NativeModule path',
        'NativeModule module',
        'NativeModule fs',
        'Binding fs',
        'Binding constants',
        'NativeModule stream',
        'NativeModule _stream_readable',
        'NativeModule _stream_writable',
        'NativeModule _stream_duplex',
        'NativeModule _stream_transform',
        'NativeModule _stream_passthrough',
        'NativeModule console',
        'Binding tty_wrap',
        'NativeModule tty',
        'NativeModule net',
        'NativeModule timers',
        'Binding timer_wrap',
        'NativeModule _linklist',
        'Binding cares_wrap',
        'Binding signal_wrap' ],
     versions: 
      { http_parser: '1.0',
        node: '0.10.10',
        v8: '3.14.5.9',
        ares: '1.9.0-DEV',
        uv: '0.10.10',
        zlib: '1.2.3',
        modules: '11',
        openssl: '1.0.1e' },
     arch: 'x64',
     platform: 'darwin',
     argv: [ 'node', '/Users/hanbo/tmp/test.js' ],
     execArgv: [],
     env: 
      { TERM_PROGRAM: 'Apple_Terminal',
        TERM: 'xterm-256color',
        SHELL: '/bin/bash',
        TMPDIR: '/var/folders/nc/_5ll_56j0fnb28yzxzcqj6780000gq/T/',
        Apple_PubSub_Socket_Render: '/tmp/launch-FS3ke9/Render',
        TERM_PROGRAM_VERSION: '309',
        OLDPWD: '/Users/hanbo',
        TERM_SESSION_ID: 'E96D82C5-D31B-4B22-B2FC-AC53AF5D862F',
        USER: 'hanbo',
        COMMAND_MODE: 'unix2003',
        SSH_AUTH_SOCK: '/tmp/launch-DHqCvG/Listeners',
        __CF_USER_TEXT_ENCODING: '0x1F7:25:52',
        Apple_Ubiquity_Message: '/tmp/launch-h9jxfu/Apple_Ubiquity_Message',
        PATH: '/opt/local/bin:/opt/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/git/bin:/usr/local/homebrew/bin',
        PWD: '/Users/hanbo/tmp',
        LANG: 'zh_CN.UTF-8',
        SHLVL: '1',
        HOME: '/Users/hanbo',
        LOGNAME: 'hanbo',
        DISPLAY: '/tmp/launch-Vt5lf3/org.macosforge.xquartz:0',
        _: '/usr/local/bin/node' },
     pid: 2028,
     features: 
      { debug: false,
        uv: true,
        ipv6: true,
        tls_npn: true,
        tls_sni: true,
        tls: true },
     _needImmediateCallback: false,
     execPath: '/usr/local/bin/node',
     debugPort: 5858,
     _getActiveRequests: [Function],
     _getActiveHandles: [Function],
     _needTickCallback: [Function],
     reallyExit: [Function],
     abort: [Function],
     chdir: [Function],
     cwd: [Function],
     umask: [Function],
     getuid: [Function],
     setuid: [Function],
     setgid: [Function],
     getgid: [Function],
     getgroups: [Function],
     setgroups: [Function],
     initgroups: [Function],
     _kill: [Function],
     _debugProcess: [Function],
     _debugPause: [Function],
     _debugEnd: [Function],
     hrtime: [Function],
     dlopen: [Function],
     uptime: [Function],
     memoryUsage: [Function],
     binding: [Function],
     _usingDomains: [Function],
     _tickInfoBox: { '0': 0, '1': 0, '2': 0 },
     _events: { SIGWINCH: [Function] },
     domain: null,
     _maxListeners: 10,
     EventEmitter: { [Function: EventEmitter] listenerCount: [Function] },
     _fatalException: [Function],
     _exiting: false,
     assert: [Function],
     config: { target_defaults: [Object], variables: [Object] },
     nextTick: [Function: nextTick],
     _nextDomainTick: [Function: _nextDomainTick],
     _tickCallback: [Function: _tickCallback],
     _tickDomainCallback: [Function: _tickDomainCallback],
     _tickFromSpinner: [Function: _tickFromSpinner],
     maxTickDepth: 1000,
     stdout: [Getter],
     stderr: [Getter],
     stdin: [Getter],
     openStdin: [Function],
     exit: [Function],
     kill: [Function],
     addListener: [Function],
     on: [Function],
     removeListener: [Function],
     mainModule: 
      { id: '.',
        exports: {},
        parent: null,
        filename: '/Users/hanbo/tmp/test.js',
        loaded: false,
        children: [],
        paths: [Object] } },
  GLOBAL: [Circular],
  root: [Circular],
  Buffer: 
   { [Function: Buffer]
     isEncoding: [Function],
     poolSize: 8192,
     isBuffer: [Function: isBuffer],
     byteLength: [Function],
     concat: [Function] },
  setTimeout: [Function],
  setInterval: [Function],
  clearTimeout: [Function],
  clearInterval: [Function],
  setImmediate: [Function],
  clearImmediate: [Function],
  console: [Getter],
  name: 'foo' }
这个是node调用函数的一个根对象。当直接调用一个函数如f1()时,由该对象负责调用。

接下来我们把上述代码中第一行定义的全局变量 name = 'foo' 改为局部变量定义: var name = 'foo'

发现在根对象中没有了name属性。

javscript中全局变量在整个工程内都是全局可见的,运行如下代码:

在test1.js文件中:

exports.test1 = function(){
	console.log(name);
};

在test.js文件中:

var test1 = require('./test1');
name = 'foo'
test1.test1();
执行:node test.js 结果得到: foo。

关于全局变量的问题,还有一些深入要讨论的,鉴于这里有点偏离主题,将在下一篇继续讨论。

回到先前的问题上来:

2.提到上下文对象this,就不得不说call和apply这两个方法。它们的作用是以不同的对象作为上下文来调用某个函数。

在函数中使用this,会把调用该函数的对象来作为上下文,而使用call或apply方法则可以为函数指定上下文:

var user = {
	name: 'foo',
	show: function(obj){
		console.log(this.name + ' doesnt give a ' + obj);
	}
};

var bar = {
	name: 'bar'
};

user.show.call(bar, 'shit'); //输出:bar doesnt give a shit
对于call方法,括号里第一个参数是绑定的上下文对象,后面的参数是调用函数的参数。每次需要绑定指定上下文都要显式地调用call方法。

如果使用bind函数,则可实现永久绑定:

var user = {
	name: 'foo',
	show: function(obj){
		console.log(this.name + ' doesnt give a ' + obj);
	}
};

var bar = {
	name: 'bar'
};

bar.func = user.show;
bar.func('duck');

bar.func1 = user.show.bind(user, 'duck');
bar.func1();
输出:

bar doesnt give a duck
foo doesnt give a duck

这样使用bind绑定上下文之后,不管是谁调用,其上下文都是确定的。同时也看到了,bind方法也具有永久绑定参数列表的作用。


关于闭包:

1. 由之前说的静态作用域我们知道,对于函数内部定义的局部变量,函数外部是不能访问的,而一个函数可以访问定义在其外部的变量。

因此,当我们想访问一个函数的内部变量时,可以通过在该函数内部再定义一个函数,然后返回这个内部函数即可。

var func = function(){
	var num = 123;
	var count = 1;

	var get = function(){
		count ++;
		num --;
		console.log(num);
		return count;
	};
	return get;
};

var counter = func();
console.log(counter());
console.log(counter());
运行以上代码得到结果:

122
2
121
3
当一个函数被调用并返回一个内部定义的函数的时候,就产生了闭包。闭包包括被返回的函数以及这个函数的定义环境。

2. 闭包的作用:

闭包主要有两大作用,第一个是实现回调函数嵌套,第二个是隐藏对象细节。

关于第一个作用在之后的文章会做进一步剖析。因为还需要探究一下回调函数的实现机理。

对于隐藏对象细节,可以这么理解:

因为函数内部变量对于外部是不可见的,我们可以通过闭包的形式,创建一个函数对象,函数内部数据外部无法直接访问,只能通过内部定义的访问器

函数来操作返回内部数据。

  相关解决方案