最近在系统的学习JS深层次内容,并稍微整理了一下,作为备忘和后期复习,这里分享给大家,希望对大家有所帮助。如有错误请留言指正,tks。

了解这些问题,我先一步步来看,先从稍微浅显内容说起,然后引出这些概念。

本文只用实例验证结果,并做简要说明,给大家增加些印象,因为单独一项拿出来都需要大篇幅讲解。

1.值类型 & 引用类型

function show(x) {
console.log(typeof(x)); // undefined
console.log(typeof(10)); // number
console.log(typeof('abc')); // string
console.log(typeof(true)); // boolean console.log(typeof(function() {})); //function console.log(typeof([1, 'a', true])); //object
console.log(typeof({
a: 10,
b: 20
})); //object
console.log(typeof(null)); //object
console.log(typeof(new Number(10))); //object
}
show();

undefined, number, string, boolean 属于简单的值类型,不是对象;
函数、数组、对象、null、new Number(10)都是对象;
判断一个变量是不是对象非常简单。值类型的类型判断用typeof,引用类型的类型判断用instanceof。

2. 语法糖(糖衣语法)

var obj = { a: 10, b: 20 };
var arr = [5, 'x', true];

语法糖其实是一种“快捷方式”,上面的代码是一种缩写,完整的写法是:

var obj = new Object();
obj.a=10;
obj.b=20; var arr= new Array();
arr[0]=5;
arr[1]='x';
arr[2]=true;

3. 对象都是通过函数创建的,而函数又是一种对象

方法一:

var fn = function() {
alert(100);
};
fn.a = 10;
fn.b = function() {
alert(123);
};
fn.c = {
name: "典橙贸易",
year: 2016
};

fn是函数,a/b/c是fn创建的对象。

方法二:

var obj = { a: 10, b: 20 };
var arr = [5, 'x', true]; 其本质是:
var obj = new Object();
obj.a = 10;
obj.b = 20; var arr = new Array();
arr[0] = 5;
arr[1] = 'x';
arr[2] = true; 而
console.log(typeof (Object)); // function
console.log(typeof (Array)); // function

4. 构造函数

只有构造函数才有prototype属性。
通常我们自定义的函数都属于构造函数,所以都有此属性。
JS运行时环境内置的函数有些不是构造函数,比如alert和Math.sqrt等,就没有此属性。
注:构造函数是指有一个内部属性[[Construct]],通过new可以创建对象的那些函数。

5. prototype 与 __proto__

每个函数function都有一个prototype,即原型。
每个对象都有一个__proto__,即隐式原型。
只有构造函数才有prototype属性。
对象的__proto__指向的是创建它的函数的prototype。
函数的prototype都是被Object创建的,每个自定义函数创建之初,都会有一个prototype(它是如何被创建的,不得而知!)。

例如:

function Foo(){};
var foo = new Foo();
Foo.__proto__ === Function.prototype // 任何函数都是由Function创建的,所以任何函数的__proto__都指向Function的prototype
foo.__proto__ === Foo.prototype // 对象的__proto__指向的是创建它的函数的prototype
Foo.prototype.__proto__ === Object.prototype // 如果是自定义的函数,它的prototype.__proto__ 指向 Object.prototype,因为自定义函数的prototype本质上就是和语法糖 var obj = {} 是一样的,都是被Object创建。
Object.prototype.__proto__ === null // Object.prototype也是个对象,它的__proto__是null(这是个特例,切记切记) foo.prototype == undefined //只有函数才有prototype属性,foo是对象
Object.prototype === (new Object()).__proto__ // 如上所述,Object本身是一个函数,(new Object())是对象
Function.prototype.__proto__ === Object.prototype // Function是一个函数,它的prototype是被Object创建,所以它的Prototype.__proto__指向创建Object.prototype
Function.__proto__ === Function.prototype //Function也是一个函数,函数是一种对象,也有__proto__属性。既然是函数,那么它一定是被Function创建。所以——Function是被自身创建的
Object.__proto__ === Function.prototype //Object是个函数,也是对象,它的__proto__指向的是创建它的函数的prototype

参考:

http://www.cnblogs.com/wangfupeng1988/p/3978131.html
http://www.cnblogs.com/wangfupeng1988/p/3979290.html

6. instanceof原理

Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
Instanceof的判断规则是:
  沿着A的__proto__这条线来找,如果B的prototype在这条线上,那么就返回true,否则返回false。

如:

function Foo(){}
var f1 = new Foo();
console.log(f1 instanceof Foo); // true
console.log(f1 instanceof Object); // true

分析方法:
  f1的__proto__路线A:

(1) f1.__proto__ === Foo.prototype
(2) Foo.prototype.__proto__ === Object.prototype
(3) Object.prototype.__proto__ === null //到终点

结论:
  f1 instanceof Foo :A线路(1)中出现了Foo.prototype
  f1 instanceof Object :A线路(2)中出现了Object.prototype

参考:

http://www.cnblogs.com/wangfupeng1988/p/3979533.html

7. 原型链

访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。
例如:

function Foo(){}
var f1 = new Foo();
f1.a = 2;
f1.b = 3;
Foo.prototype.b = 6;
Foo.prototype.c = 7; console.log(f1.a); // 2,a是f1的基本属性,直接访问没问题
console.log(f1.c); // 7,f1的基本属性中没有c,沿着原型链向上,f1.__proto__是Foo.prototype,而Foo.prototype有c,可以访问。
console.log(f1.b); // 3,b是f1的基本属性,直接访问没问题。可Foo.prototype还有一个b,但是不会显示,这种情况被称为“属性遮蔽”。

判断属性是基本属性(也叫实例属性)还是原型链上的属性(也叫原型属性)使用hasOwnProperty,一般在for…in..循环中。for…in…会输出实例属性和原型属性。
属性列表参考实例属性 vs 原型的属性 vs 静态属性
如:

var item;
for(item in f1){
console.log(item); // a,b,c
} for(item in f1){
f1.hasOwnProperty(item) && console.log(item); // a,b
}

8. 继承

在原型链的基础上,很容易理解继承。例如在原型链的实例中,f1.hasOwnProperty就是继承自Object.prototype。
过程如下:

  • f1基本属性中没有hasOwnProperty,沿原型链向上找:f1.__proto__是Foo.prototype;
  • Foo.prototype中也没有hasOwnProperty,继续沿原型链向上找:Foo.prototype.__proto__是Object.prototype;
  • Object.prototype中有hasOwnProperty,查找结束,允许访问hasOwnProperty。

由于所有的对象的原型链都会找到Object.prototype,因此所有的对象都会有Object.prototype的方法。这就是所谓的“继承”。
另外,每个函数都有的call,apply方法,和length,arguments,caller等属性也都是继承下来的。

9. 执行上下文/执行上下文环境

定义1:js在执行一段代码之前,会声明提前,赋值或赋初始值(即undefined)。所以同一代码段中,可以先访问,再定义,这就是“执行上下文”。
定义2:在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。

分4种情况:

第1种情况:变量、函数表达式——只提前声明,不提前赋值,默认赋值为undefined;

console.log(a); // undefined
var a=10;
console.log(fn1); // undefined
var fn1 = function(){};

第2种情况:this关键字——提前声明并赋值

console.log(this); // Window {external: Object, chrome: Object, document: document, __ctrip: Object, __SERVERDATE__: Object…}

第3种情况:函数声明——提前声明并赋值

console.log(fn2); // function fn2(){}
function fn2(){};

第4种情况:函数内部会创建自己独有的上下文环境

函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。
另外,每个函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。
如:

var a = 10;

function fn() {
console.log(a); // a是自由变量,函数创建时,就确定了a要取值的作用域
} function bar(f) {
var a = 20;
f(); // 10,到创建fn的地方找变量a的作用域,而不是调用它的当前作用域。
}
bar(fn);

10. 执行上下文

如【执行上下文/执行上下文环境】所述,js代码中,会有无数的函数调用和嵌套调用,会产生无数的上下文环境,这么多上下文环境如何管理,以及如何销毁而释放内存呢?这就需要【执行上下文栈】的参与了。

执行上下文栈:执行全局代码时,会产生一个全局上下文环境,并将该上下文环境压入栈,每次调用函数都又会产生执行上下文环境,并将该函数上下文环境也压入栈。当函数调用完成时,该函数上下文环境出栈,并消除该函数上下文环境以及其中的数据,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。其实这是一个压栈出栈的过程。

如:

var a = 10, // 1. 进入全局上下文环境
fn,
bar = function(x) {
var b = 5;
fn(x + b); // 3. 进入fn函数上下文环境,并入栈,设为活动状态。该函数执行完毕后,bar函数上下文出栈,并及时销毁,释放内存
}; fn = function(y) {
var c = 5;
console.log(y + c);
} bar(10); // 2. 进入bar函数上下文环境,并入栈,设为活动状态。该函数执行完毕后,bar函数上下文出栈,并及时销毁,释放内存

参考:http://www.cnblogs.com/wangfupeng1988/p/3989357.html

11. this

  this在执行上下文中有着非常重要的作用,这里应该说一下。

  了解this,需要记住一点:在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。

  大致分为以下4种情况:

情况1:构造函数,this就代表new出来的对象。

function Foo(){
this.name = '典橙贸易';
this.year = 2016;
console.log(this); // Foo {name: "典橙贸易", year: 2016}
}
var f1 = new Foo(); // 所谓构造函数就是用来new对象的函数。

另外,如果在原型链中使用this(Foo.prototype中使用this),this仍然代表new出来的对象。

function Foo(){
this.name = '典橙贸易2016';
}
Foo.prototype.getName = function(){
console.log(this); // Foo {name: "典橙贸易2016"}
}
var f1 = new Foo();
f1.getName();

情况2:函数作为对象的一个属性,并且被对象直接调用时,this指向该对象。

var obj = {
name:'典橙贸易',
fn:function(){
console.log(this); // Object {name: "典橙贸易"}
console.log(this.name); // 典橙贸易
}
}
obj.fn(); // 直接调用 // 如果不是直接调用,this就指向window
var obj = {
name: '典橙贸易',
fn: function() {
console.log(this); // Window {external: Object, chrome: Object, document: document, __ctrip: Object, __SERVERDATE__: Object…}
console.log(this.name); // undefined
}
}
var fn1 = obj.fn;
fn1();

情况3:函数用call或者apply调用,this就是传入的对象的值

var obj ={
name:'典橙贸易'
}
var fn = function(){
console.log(this); // Object {name: "典橙贸易"}
console.log(this.name); // 典橙贸易
}
fn.call(obj);
// 或者
// fn.apply(obj);

情况4:全局 & 调用普通函数,this是window

全局:

console.log(this===window); // true

调用普通函数:
var name ="典橙贸易";
var fn = function() {
console.log(this); // Window {external: Object, chrome: Object, document: document, __ctrip: Object, __SERVERDATE__: Object…}
console.log(this.name); // 典橙贸易
}
fn();

注意:

var obj = {
name: '典橙贸易',
fn: function() { function f() {
console.log(this); // Window {external: Object, chrome: Object, document: document, __ctrip: Object, __SERVERDATE__: Object…}
console.log(this.name); // undefined
}
f(); // 函数f虽然是在obj.fn内部定义的,但是它仍然是一个普通的函数,this仍然指向window。 }
}
obj.fn();

12. 作用域

知识点1:javascript中没有块级作用域。

var i = 2;
if(i>1){
var name ='典橙贸易';
}
console.log(name); // 典橙贸易
for循环的{}也是类似。

知识点2:javascript除了全局作用域之外,只有函数可以创建自己的作用域,称为函数作用域。

如经典的问题:

<ul id = "list">
<li> we </li>
<li> sdf </li>
<li> cx </li>
<li> h </li>
<li> z </li>
</ul>
var list = document.getElementById('list');
var e = list.getElementsByTagName('li');
var i = 0; 错误写法:
for (; i < e.length; i++) {
e[i].onclick = function() {
console.log(i);
}
} 正确写法:
for (; i < e.length; i++) {
(function(a) {
e[i].onclick = function() {
console.log(a);
}
})(i);
}
等效于:
for (; i < 5; i++) {
var Fn = function(a) {
e[i].onclick = function() {
console.log(a);
}
}
Fn(i);
}

错误的原因:
  e[i].onclick 每次循环,只是赋值给onclick,而for循环是没有块级作用域的,所以i的值会不断累加,直到最大值5,故每次循环都会输出5。
正确的原因:
  由于函数可以创建自己的作用域,而且各个作用域间不会相互影响,所以每次循环Fn(i)都会创建一个特有的函数作用域提供给相应的onclick,而每个作用于中的a变量也不会相互影响。

知识点3:作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

{
var a = 1,
b = 2; function fn1() {
var a = 100,
b = 200; function fn2() {
var a = 1000,
b = 2000;
}
}

三个作用域下都声明了“a和b”变量,但是他们不会有冲突。各自的作用域下,用各自的“a和b”。

13. 自由变量及其取值规则

在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个自由变量。
如:

var x=1;
function fn(){
var b=2;
console.log(x+b); // 这里的x就是自由变量
}

自由变量取值规则:要到创建 自由变量所在函数 的那个作用域中取值–是【创建】,而不是【调用】,也不是【父作用域】。

如:

var x = 10;
function fn() {
console.log(x); // fn创建了自由的函数作用域,所以无论什么地方调用它,都会输出10
}
function show(f) {
var x = 20;
(function() {
console.log(x); // 要到x所在的匿名函数的作用域中找x,故输出20
f(); // 要到创建fn函数的作用域中找x,故这里输出10,而不是20
})();
}
show(fn);

14. 作用域链

在自由变量的基础上理解作用域链更加容易。
在作用域Fn中使用一个变量A,如果Fn中没有定义A,则到创建Fn的那个作用域Fm中找,如果仍没有找到,则继续到创建Fm的作用域中找……依次类推,直至找到变量A或者到全局作用域中仍未找到 为止,这种跨越多个作用域的线路,就叫“作用域链”。
代码如下:

{
// var A = 4; // 第4步,这是全局作用域,找到则返回4,如果到这里仍未找到,就结束,报错"A is not defined" function Fw() {
// var A = 3; // 第3步,在作用域Fw中找,找到则返回3,否则到创建Fw的全局作用域中找 function Fm() {
// var A = 2; // 第2步,在作用域Fm中找,找到则返回2,否则到创建Fm的Fw中找 function Fn() {
// var A = 1; // 第1步,在当前作用域找,找到则返回1,否则到创建Fn的Fm中找
console.log(A);
}
return Fn();
}
return Fm();
}
Fw();
}

注意:

  这里说的创建Fn的那个作用域,而不是调用Fn的那个作用域,也不是“父作用域”。详情参考【自由变量及其取值规则 > 自由变量取值规则】中的实例。

15. 闭包

要想理解闭包,前面的【作用域、自由变量、作用域链】三部分是基础。
闭包的两种形式:函数作为返回值函数作为参数被传递

第一,函数作为返回值

function fn() {
var max = 10;
return function bar(x) {
if (x > max) { // max是自由变量,取值规则,参考【自由变量及其取值规则】
console.log(x);
}
}
}
var f1 = fn();
f1(15); //

第二,函数作为参数被传递

var max = 10,
fn = function(x) {
if (x > max) { // max是自由变量,取值规则,参考【自由变量及其取值规则】
console.log(x);
}
};
(function(f) {
var max = 100;
f(15); //
})(fn)

另外,
在【执行上下文栈】中说到,每个函数执行完毕,都会销毁其函数上下文环境,并清空数据。但是闭包函数执行完后,上下文环境不会被销毁。因为闭包中函数会返回或者作为参数被传递,在其他地方会被用到,一旦销毁,调用闭包的地方就无法再使用了。所以闭包会增加内容开销。

参考:

  http://www.cnblogs.com/wangfupeng1988/p/3994065.html

  http://blog.csdn.net/yueguanghaidao/article/details/9568071

  http://blog.csdn.net/lmj623565791/article/details/25076713

JavaScript高级内容笔记:原型链、继承、执行上下文、作用域链、闭包的更多相关文章

  1. JavaScript高级内容:原型链、继承、执行上下文、作用域链、闭包

    了解这些问题,我先一步步来看,先从基础说起,然后引出这些概念. 本文只用实例验证结果,并做简要说明,给大家增加些印象,因为单独一项拿出来都需要大篇幅讲解. 1.值类型 & 引用类型 funct ...

  2. Js 作用域与作用域链与执行上下文不得不说的故事 ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄

    最近在研究Js,发现自己对作用域,作用域链,活动对象这几个概念,理解得不是很清楚,所以拜读了@田小计划大神的博客与其他文章,受益匪浅,写这篇随笔算是自己的读书笔记吧~. 作用域 首先明确一个概念,js ...

  3. javascript中词法环境、领域、执行上下文以及作业详解

    词法环境(Lexical Environments) 官方规范对词法环境的说明是:词法环境(Lexical Environments)是一种规范类型,用于根据ECMAScript代码的词法嵌套结构来定 ...

  4. Javascript高级程序设计笔记(很重要尤其是对象的设计模式与继承)

    var obj = {'a':'a'}; var fun = function (){} console.log(typeof obj);//object console.log(typeof fun ...

  5. JavaScript高级程序设计笔记之面向对象

    说起面向对象,大部分程序员首先会想到 类 .通过类可以创建许多具有共同属性以及方法的实例或者说对象.但是JavaScript并没有类的概念,而且在JavaScript中几乎一切皆对象,问题来了,Jav ...

  6. javascript高级编程笔记02(基本概念)

    ParseInt()函数: 由于Number函数在转换字符串时比较复杂而且不合理,我们常常转换字符串都用parseInt函数, Parseint函数规则: 忽略字符串前面的空格,直到找到第一个非空格字 ...

  7. javascript高级编程笔记01(基本概念)

    1.在html中使用JavaScript 1.  <script> 元素 <script>定义了下列6个属性: async:可选,异步下载外部脚本文件. charset:可选, ...

  8. JavaScript学习3:原型和继承

    原型 我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包括能够由特定类型的全部实例共享的属性和方法.逻辑上能够这么理解:prototype是通过调用构造函数而创 ...

  9. Javascript高级程序设计——面向对象之实现继承

    原型链: 构造函数中都有一个prototype属性指针,这个指针指向原型对象,而创建的实例也有指向这个原型对象的指针__proto__.当实例查找方法时先在实例上找,找不到再通过__proto__到原 ...

随机推荐

  1. PHP之变量范围

    前面的话 变量范围即它定义的上下文背景(也就是它的生效范围).在javascript中,并没有变量范围这一概念,相似的可能是作用域.但是,由于javscript使用的是词法作用域,指变量声明时的位置: ...

  2. 深入浅出Docker(三):Docker开源之路

    背景 Docker从一开始的概念阶段就致力于使用开源驱动的方式来发展,它的成功缘于国外成熟的开源文化氛围,以及可借鉴的社区运营经验.通过本文详细的介绍,让大家可以全面了解一个项目亦或者一项技术是如何通 ...

  3. vue-学习笔记(更新中...)

    vue学习笔记 2017-08-23  11:10:28 Vue实例: var vm = new Vue({ // 选项 }) 实例化Vue.Vue实例,构造函数Vue.创建一个Vue的根实例,Vue ...

  4. LeetCode——Add Digits

    Description: Given a non-negative integer num, repeatedly add all its digits until the result has on ...

  5. Eclipse failed to get the required ADT version number from the sdk

    failed to get the required ADT version number from the sdk 解决方法: eclipse 和 android studio 工具不能同时共用同一 ...

  6. windows服务的默认启动类型和登录帐户

    转自:http://www.winhelponline.com/blog/windows-7-services-default-startup-type/ Service Name Startup T ...

  7. ajax解决跨域方法(适用于自己写接口解决跨域)

    原因是这样的:最近用PHP开发了一个网站,这个网站需要提供接口,接口开发完成之后,在本地进行请求,跨域测试. jsonp处理跨域和用PHP函数来处理跨域就不说了. 现在说的使用用 header 这个来 ...

  8. 阿里云ecs禁止ping,禁止telnet

    现在的中小型企业服务器大多是云比较多,因此,可能会面临着服务器ping不通,或者是端口telnet不通的情况,但是服务器上的服务仍然是正常的情况,这个时候我们就要考虑是不是云上配置了访问规则了.废话不 ...

  9. vuls漏洞扫描工具

    Vuls 是一款适用于 Linux/FreeBSD 的漏洞扫描程序,无代理,采用 Go 语言编写,对于系统管理员来说,每天必须执行安全漏洞分析和软件更新都是一个负担. 为避免生产环境宕机,系统管理员通 ...

  10. EL表达式经验教训 javax.el.PropertyNotFoundException 出错

    之所以是把他记下来,是因为这个低级错误 害的我找了老半天. 后台传了对象到页面,在页面中循环遍历获得对象某个属性值 如下: <c:forEach items="${resultMap. ...