深入理解javascript原型和闭包

原文链接http://www.cnblogs.com/wangfupeng1988/p/3977924.html

对象是属性的集合。

function show(x) {console.log(typeof(x));    // undefinedconsole.log(typeof(10));   // numberconsole.log(typeof('abc')); // stringconsole.log(typeof(true));  // booleanconsole.log(typeof(function () { }));  //functionconsole.log(typeof([1, 'a', true]));  //objectconsole.log(typeof ({ a: 10, b: 20 }));  //objectconsole.log(typeof (null));  //objectconsole.log(typeof (new Number(10)));  //object}
show();

以上代码列出了typeof输出的集中类型标识,其中上面的四种(undefined, number, string, boolean)属于简单的值类型,不是对象。剩下的几种情况——函数、数组、对象、null、new Number(10)都是对象。他们都是引用类型。
判断一个变量是不是对象非常简单。值类型的类型判断用typeof,引用类型的类型判断用instanceof。

对象都是通过函数来创建的

函数就是对象的一种,因为通过instanceof函数可以判断。

var fn = function () { };
console.log(fn instanceof Object);  // true
var obj = { a: 10, b: 20 };
var arr = [5, 'x', true];

这是种语法糖,是一种快捷方式。真实代码是:

//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;

而其中的 Object 和 Array 都是函数。说明一切对象都是由函数创建的。
对象是函数创建的,而函数却又是一种对象

typeof(Array)//"function"
typeof([1,2])//"object",[1,2]其实是new Array();
typeof(function() {})//"function"

prototype原型

函数有prototype属性,对象有_proto_属性。
每个函数都有一个属性叫做prototype。这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身。
如上图,SuperType是是一个函数,右侧的方框就是它的原型。
clipboard.png
接着往下说,你也可以在自己自定义的方法的prototype中新增自己的属性

function Fn() { }
Fn.prototype.name = '王福朋';
Fn.prototype.getYear = function () {return 1988;
};

还有一种很容易被混淆的定义函数方法:

var func = function() {}
func.prototype.name = "prototype test";
func.prototype.protoFunc = function() {console.log("protoFunc")};
func.a = function() {console.log("定义对象的属性")}
func.a();//func是个对象,func是个引用。
var funcObject = new func();
console.log(new func(), "new func()");//function() {}
console.log(funcObject, "funcObject");//function() {}
funcObject.name;
funcObject.protoFunc();
funcObject.a();//funcObject.a is not a function(…),//func是个对象,有a这个属性,
//funcObject是个对象,但是a不是funcObject的属性。func的所有prototype的属性就是funcObject的属性。
function Fn() {};
Fn.prototype.protoFunc = function() {return "aa";console.log("protoFunc")};
Fn.a = function() {}
var fn= new Fn();
fn.protoFunc() //"aa"
fn.a();//FnObject 不是Fn对象,所以a()方法没定义。

即,Fn是一个函数,fn对象是从Fn函数new出来的,这样fn对象就可以调用Fn.prototype中的属性。
因为每个对象都有一个隐藏的属性——“__proto__”,这个属性引用了创建这个对象的函数的prototype。即:fn.__proto__ === Fn.prototype.这里的"__proto__"成为“隐式原型”

继承(原型链)

function Foo() {}
var f1= new Foo();
f1.a = 10
Foo.prototype.a = 100;
Foo.prototype.b = 200;
console.log(f1.a)//10
console.log(f1.b)//200

f1是Foo函数new出来的对象,f1.a是f1对象的基本属性,f1.b是怎么来的呢?——从Foo.prototype得来,因为f1.__proto__指向的是Foo.prototype
访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。

灵活的原型链

如果你要添加内置方法的原型属性,最好做一步判断,如果该属性不存在,则添加。如果本来就存在,就没必要再添加了。

继承--原型链

clipboard.png

理解这张图有以下几点:
1.一切皆对象,所以每个对象都有_proto_属性,_proto_属性指向创建该对象的函数的prototype。
2.prototype的属性值是一个对象(默认的只有一个叫做constructor的属性,指向这个函数本身)。所以各函数的prototype也有_proto_属性。
3.Object函数的_proto_只想null。
4.var Foo = new Function();则Foo._proto_指向Function.prototype.
5.function Function(){}是有它自己创造的,所有Function._proto_指向Function.prototype.

执行上下文

在“准备工作”中完成了哪些工作:

console.log(a)//a is not definedconsole.log(a)//undefined
var a;console.log(a)//undefined
var a = 10;console.log(a)//function a() {}函数声明
function a() {}console.log(a)//undefined 函数表达式
var a = function(){}console.log(this)//Window {external: Object, chrome: Object, document: document, lock: Object, SF: Object…} 
//this在任意环境下都有值
  1. 变量、函数表达式--变量声明,默认赋值为undefined;

  2. this——赋值

  3. 函数声明——赋值;
    这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。

clipboard.png

给执行上下文环境下一个通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。

this

在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。
情况1:构造函数
所谓构造函数就是用来new对象的函数。其实严格来说,所有的函数都可以new一个对象,但是有些函数的定义是为了new一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)。例如:Object、Array、Function等。

function Foo() {this.name = "name";this.year = "year";console.log(this);
}var f1 = new Foo();//Foo {name: "name", year: "year"}
Foo();//Window {external: Object, chrome: Object, document: document, lock: Object, SF: Object…}

如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。
如果直接调用Foo函数,而不是new Foo(),这种情况下this是window。

情况2:函数作为对象的一个属性

var obj = {x:10,fn:function(){console.log(this);    console.log(this.x);}
}var fn1 = obj.fn;
fn1();//Window {external: Object, chrome: Object, document: document, lock: Object, SF: Object…} undefined
obj.fn();//Object {x: 10}  10

fn不仅作为一个对象的一个属性,而且的确是作为对象的一个属性被调用。结果this就是obj对象。
注意,如果fn函数不作为obj的一个属性被调用。如上代码,如果fn函数被赋值到了另一个变量中,并没有作为obj的一个属性被调用,那么this的值就是window,this.x为undefined。

情况3:函数用call或者apply调用
当一个函数被call和apply调用时,this的值就取传入的对象的值。

var obj = {x : 1
}var fn = function() {console.log(this);console.log(this.x);
}fn.call(obj);//Object {x: 1} 1

情况4:调用普通函数

var obj = {x:10,fn:function(){console.log(this);    //Object {x: 10}console.log(this.x);    //10function f() {console.log(this);    // Window {external: Object, chrome: Object, document: document, lock: Object, SF: Object…}console.log(this.x);//undefined}f();}
}obj.fn();

函数f虽然是在obj.fn内部定义的,但是它仍然是一个普通的函数,this仍然指向window。

简介【作用域】

你光知道“javascript没有块级作用域”是完全不够的,你需要知道的是——javascript除了全局作用域之外,只有函数可以创建的作用域。
所以,我们在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好。除了这两个地方,其他地方都不要出现变量声明。而且建议用“单var”形式。
jQuery源码的最外层是一个自动执行的匿名函数:

clipboard.png
为什么要这样做呢?

原因就是在jQuery源码中,声明了大量的变量,这些变量将通过一个函数被限制在一个独立的作用域中,而不会与全局作用域或者其他函数作用域的同名变量产生冲突。
全世界的开发者都在用jQuery,如果不这样做,很可能导致jQuery源码中的变量与外部javascript代码中的变量重名,从而产生冲突。

闭包

但是你只需要知道应用的两种情况即可——函数作为返回值,函数作为参数传递。

function fn(){var max = 10;return function bar(x){if(x > max) {console.log(x);}};
}var f1 = fn();
f1(15);//15

如上代码,bar函数作为返回值,赋值给f1变量。执行f1(15)时,用到了fn作用域下的max变量的值。

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

var max = 10;
fn = function(x){if(x > max) {console.log(x);}
};(function(f) {var max = 100;f(15);//15
})(fn);

如上代码中,fn函数作为一个参数被传递进入另一个函数,赋值给f参数。执行f(15)时,max变量的取值是10,而不是100。
自由变量跨作用域取值时,要去创建这个函数的作用域取值,而不是“父作用域”。
当一个函数被调用完成之后,其执行上下文环境将被销毁,其中的变量也会被同时销毁。但是有些情况下,函数调用完成之后,其执行上下文环境不会接着被销毁。这就是需要理解闭包的核心内容。
对于这个例子

function fn(){var max = 10;return function bar(x){if(x > max) {console.log(x);}};
}var f1 = fn();
max = 100;
f1(15);//15

fn()调用完成。按理说应该销毁掉fn()的执行上下文环境,但是这里不能这么做。注意,重点来了:因为执行fn()时,返回的是一个函数。函数的特别之处在于可以创建一个独立的作用域。而正巧合的是,返回的这个函数体中,还有一个自由变量max要引用fn作用域下的fn()上下文环境中的max。因此,这个max不能被销毁,销毁了之后bar函数中的max就找不到值了。

总结:跟着大牛的文章,跟着理解,跟着写代码,终于把闭包理解了。但是,理论是学到了,真正用的时候还得要多思考。这种逻辑的碰撞就是程序员向前最大的鼓励。谢谢大牛。
写给自己的话:过早地开始关注细节,你很可能错失上下文或整体信息。当然,错失了细节,也会让你的理解仅仅停留在一些事物的表面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/284528.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

薪资高压线

阅读本文大概需要5分钟。最近一名读者咨询一个问题:洋哥,最近公司有一名同事因为打探其他人薪资被开除了,为啥我们公司要把薪资设置为高压线。这是个好问题,解答完他的疑惑后想起了一年多前写过一篇,彼时读者还比较少&…

达摩院年终预测出炉:2022 十大科技趋势,AI for Science 高居榜首

作为“一所探索科技未知的研究院”,阿里巴巴达摩院成立至今已经四年了。 这四年来,达摩院秉持着“探索科技位置,以人类愿景为驱动力,开展基础科学和颠覆式技术创新研究”的原则与使命,在基础科研和硬科技发展上“遍地生…

chrome调试工具高级不完整使用指南(基础篇)

一、前言 本文记录的是作者在工作上面对chrome的一些使用和情况的分析分享,内容仅代表个人的观点。转发请注明出处(http://www.cnblogs.com/st-leslie/),谢谢合作 二、浏览器模块介绍 由于chrome浏览器一直在不断的进行更新迭代,会不断的新增功能&#x…

新型基础测绘与实景三维中国建设技术文件【2】基础地理实体分类、粒度及精度基本要求

《新型基础测绘体系建设试点技术大纲》指出,新型基础测绘将以“基础地理实体”为核心的成果模式创新为切入点,带动技术体系、生产组织体系和政策标准体系的全面创新,从而实现基础测绘高质量发展。 基础地理实体作为新型基础测绘产品体系的核心…

构建和实现单点登录解决方案(转载于IBMdeveloperWorks)

将一个开放源码的基于 Java 的身份验证组件集成进 Web 门户中 在现有的应用程序中实现单点登录解决方案(single sign-on,SSO,即登录一次,就可以向所有网络资源验证用户的身份)是非常困难的,但是在构建复杂的…

分享一个基于Abp 和Yarp 开发的API网关项目

这个项目起源于去年公司相要尝试用微服务构建项目,在网关的技术选型中,我们原本确认了ApiSix 网关,如果需要写网关插件需要基于Lua脚本去写,我和另外一个同事当时基于这个写了一个简单的插件,但是开发测试以及发布都很麻烦,而且使用Lua脚本作为插件的开发语言本身也不是我们强项…

罗振宇2022“时间的朋友”跨年演讲全文稿(pdf)

2021年12月31日20:30,五粮液成都金融城演艺中心,罗振宇“时间的朋友”跨年演讲如约而至。 罗胖曾发下大愿望:跨年演讲要连办二十年。今年是第七场,也是最特殊的一场,罗胖面对12000个空座位,用53个好故事&am…

08.LoT.UI 前后台通用框架分解系列之——多样的Tag选择器

LOT.UI分解系列汇总:http://www.cnblogs.com/dunitian/p/4822808.html#lotui LoT.UI开源地址如下:https://github.com/dunitian/LoTCodeBase/tree/master/LoTUI 这个必须说下,本来是用Bootstrap-Select做的,很漂亮,正好…

jquery文档加载完毕后执行的几种写法

2019独角兽企业重金招聘Python工程师标准>>> 1.js文档加载完毕 标签内 οnlοad"test()"window.οnlοadfunction(){}2.jquery文档加载完毕 //方式1 $(document).ready(function(){//TODO }); //方式2 $(function(){//TODO }) //方式3 $(function($){//TO…

新型基础测绘与实景三维中国建设技术文件【3】基础地理实体空间身份编码规则

基础地理实体是新型基础测绘产品体系中的核心成果,是推动基础测绘工作转型升级的关键。与现有的测绘地理信息数据不同,基础地理实体具有多粒度、多模态、多层次,以及搭载结构化、半结构化和非结构化多样化信息的鲜明特点。 基础地理实体空间…

oracle 表 视图 存储过程 序列 job

table 表--delete tabledrop table Test1;-- Create tablecreate table TEST1(ID NUMBER,T_NAME VARCHAR2(100),DT DATE);-- 添加注释comment on column TEST1.T_NAME is 名称;--添加age字段alter table Test1 add (age NUMBER(8));--删除字段alter table TABLE_NAME …

[转]Docker 大势已去,Podman 即将崛起

Podman Podman 什么是Podman?Podman和Docker的主要区别是什么?Podman的使用与docker有什么区别?Podman 常用命令 容器镜像部署 PodmanPodman 加速器使用 Podman 运行一个容器列出运行的容器检查正在运行的容器查看一个运行中容器的日志查看一…

基于Kubernetes v1.24.0的集群搭建(一)

一、写在前面 K8S 1.24作为一个很重要的版本更新,它为我们提供了很多重要功能。该版本涉及46项增强功能:其中14项已升级为稳定版,15项进入beta阶段,13项则刚刚进入alpha阶段。此外,另有2项功能被弃用、2项功能被删除。…

思科三层交换机充当路由器实现全网互通

转载于:https://blog.51cto.com/13568840/2059797

mpvue开发小程序分享朋友圈无法自定义标题解决方法

在node_modules里面找到mpvue,手动修改一下mpvue这个包下的index.js文件 // 用户点击右上角分享 到朋友圈 onShareTimeline: rootVueVM.$options.onShareTimeline? function (options) { return callHook$1(rootVueVM, onShareTimeline, options); } : null,找到 L…

【ArcGIS Pro微课1000例】0020:ArcGIS Pro中河流(曲线)、湖泊(水体色)图例制作案例教程

相关阅读:【ArcGIS微课1000例】0032:ArcGIS中河流(曲线)、湖泊(水体色)图例制作案例教程 河流、湖泊的样式设置功能在ArcGIS Pro得到了延续,本文讲解ArcGIS Pro中河流湖泊图例的设置方法。 《ArcGIS Pro从入门到精通系列精品教程(微课版)》专栏包括完整的实验数据包,…

swift学习选pizza项目

2019独角兽企业重金招聘Python工程师标准>>> 原文: https://makeapppie.com/2014/09/18/swift-swift-implementing-picker-views/ 效果: 步骤: 新建iOS single view application 名字为SwiftPickerViewPizzaDemo, 打开main storyboard选中view controoler, 右上角, …

Windows 11 新版 25163 推送!任务栏全新菜单、应用商店更新、文件资源管理器大量修复...

面向 Dev 频道的 Windows 预览体验成员,微软现已推送 Windows 11 预览版 Build 25163。主要变化1.微软宣布为 Windows 11 任务栏引入全新溢出体验,当任务栏上的应用程序图标或窗口达到任务栏容量上限时,将启用全新溢出菜单。2.微软更新了 Mic…

[转]Web3 是去中心化的“骗局”?

作者 | InvisibleUp 译者 | 弯月 出品 | CSDN(ID:CSDNnews) Web3 不是去中心化。 虽然我觉得这一点很明显,根本不需要通过一篇文章来说明,但我也是迫不得已,因为突然之间各大科技巨头,比如 Redd…

实景三维建设背景下,三维GIS面临哪些挑战?

2021年8月26日,自然资源部印发《实景三维中国建设技术大纲(2021版)》,明确指出“实景三维中国建设是落实数字中国、平安中国、数字经济战略的重要举措,是落实国家新型基础设施建设的具体部署,是服务生态文明…