《javascript语言精粹》学习笔记之函数特性

分析javascript

javascript比较好的思想:函数、弱类型、动态对象、对象字面量表示法
不好的思想:基于全局变量的编程模型

函数

函数对象

函数就是对象,新创建的函数会连接到Function.prototype上,没和函数创建时附带有两个隐藏属性:函数上下文和实现函数行为的代码。(后者使得函数可以被调用)

函数字面量

通过函数字面量创建的函数对象包含一个连接到外部上下文的连接,被称为闭包,这是javascript强大表现力的根基。

调用

除了声明的形式参数外,函数接收两个附带的参数:this和arguments。参数this在面向对象编程中非常重要,他的值取决于调用的模式
在javascript中一共有四种调用模式:方法调用模式、函数调用模式、构造器调用模式和apply调用模式。
函数调用时如果传入的参数个数和定义的参数个数不一致,不会产生运行时错误,实际参数多于形式参数,超出的参数值会被忽略,少于的话直接被定义为undefined。

方法调用模式

当函数作为对象的一个方法被调用时,this会绑定到该对象。this到对象的绑定发生在函数调用的时候。这个超级迟邦定使得函数可以对this高度复用。通过this可取得他们所属对象的上下文的方法被成为公共方法。

函数调用模式

当一个函数并非一个对象的属性时,此时是函数调用。这时this被绑定到全局对象,这是语言设计的一个错误,这个设计错误的后果是方法不能被利用内部函数来帮他工作,因为内部函数的this被绑定到了全局对象上,并不是外部对象,不能共享方法对对象的访问权,解决这个问题的方案是在该方法中定义一个变量,将外部的this赋值给这个变量,然后在内部函数中使用。

var that = this

构造器调用模式

javascript是一门基于原型继承的语言,这意味着对象可以直接从其他对象继承属性。该语言是无类别的。
如果在一个函数前面加上new来调用,那么将创建一个隐藏连接到该函数的prototype成员的新对象,同时this将会绑定到新对象上。new前缀也会改变return语句的行为。

var Quo =  function(string){this.status = string;
}
Quo.prototype.get_status = function(){return this.status;
}
var myQuo = new Quo("confused");

但是并不推荐使用这种形式的构造器函数。在之后将会看到更好的替代方式。

Apply调用模式

因为javascript是一门函数式的面向对象编程语言,所以函数也可以拥有方法。
apply方法可以让我们传入参数去调用函数,apply方法接收两个参数。第一个是将被绑定的this的值,第二个是参数数组。

//构造一个包含 status 成员的对象,
var statusObject={
status:"A-OK
}
//statusObject并没有继承自Quo.prototype,但我们可以在 statusObject 上调
//用get status 方法,尽管 statusObject并没有一个名为 get status 的方法。
var status =Quo.prototype.getstatus.apply(statusObject);
//stats 值为'A-OK'

参数

函数会附带一个参数arguments,通过他可以访问到所有传入函数的参数,这使得我们可以实现传入任意数量参数的函数

//构造一个将很多个值相加的函数
//注意该函数内部定义的变量 sum 不会与函数外部定义的 sum 产生冲突
//该函数只会看到内部的那个变量。
var sum=function(){var i,sum=0;for(i=0;i<arguments.length;i+=l){sum += arguments[i];}return sum;
document.writeln(sum(4815162342));//108

后续将为数组添加一个相似的方法来达到同样的效果
arguments并不是一个真正的数组,他只是一个‘类似数组(array-like)’的对象。arguments拥有一个length属性,但他缺少所有的数组方法,这是语言设计的一个错误。

返回

return语句用来让函数返回。
一个函数总是会返回一个值,如果没有指定返回值,则会返回undefined。
如果函数以new前缀的方式进行调用,且返回值不是一个对象,则会返回this(该新对象)。

异常

当查出事故时,可以在try中通过throw抛出异常,throw会中断函数的执行,将控制权跳转到catch从句中

给类型增加方法

javascript允许为语言的基本类型增加方法,通过对基本类型的prototype添加方法来使得该方法对所有对象可用,这样的方法对函数、字符串、布尔值、数组、正则表达式、数字同样适用。

例如 我们可以为Function.prototype添加方法来使得该方法对所有的函数可用:

Function.prototype.method = function(name, func){this.prototype[name] = func;return this;
}

通过为Function的prototype添加一个方法,使得所有函数都可以直接调用该方法,为对应的函数的原型(不是Function)添加新的方法,并且添加时不再需要键入prototype这个属性名,这个缺点也就被掩盖了
例如,为数字类型添加一个取整数的方法,如果没有method,我们需要这样定义:

Number.prototype.integer = function(){return Math[this<0?"ceiling":"floor"](this);
}

现在有了method方法,可以直接通过method方法来实现

Number.method('integer',function(){return Math[this<0?"ceiling":"floor"](this);
}):
// 所有对应的对象都可以使用这个方法了
document.writeln((-10/3).integer());//-3

在 JavaScript 中,Number 是一个内置的构造函数,它也是一个函数对象。因此,Number 也继承自 Function.prototype。这意味着我们可以给 Function.prototype 添加方法,然后 Number 也可以使用这些方法。
通过给javascript基本类型增加方法,可以提高语言的表现力。并且javascript原型的继承是动态属性,新的方法添加后会被立即赋予到所有的值(对象实例)上,哪怕对象实例在方法创建前就已经建好了。
基本类型的原型是公共的结构,所以在类库混用时务必小心。一个保险的做法就是只在确定没有该方法时才添加它。

//有条件地增加一个方法
Function.prototype.method=function(name,func){if(!this.prototype[name]){this.prototype[name]= func;}
}

另一个要注意的就是for in语句用在原型上时表现很糟糕,他会沿着原型链遍历所有的属性名。可以使用 hasownProperty 方法去筛选出继承而来的属性,或者我们可以查找特定的类型。

递归

javascript也支持递归,去调用自身
一些语言提供了尾递归优化。这意味着如果一个函数返回自身递归调用的结果,那么调用的过程会被替换为一个循环,它可以显著提高速度。遗憾的是,JavaScript 当前并没有提供尾递归优化。深度递归的函数可能会因为返回堆栈溢出而运行失败。

作用域

在编程语言中,作用域控制着变量与参数的可见性及生命周期。对程序员来说这是一个重要的帮助,因为它减少了名称冲突,并且提供了自动内存管理。
然而javascript没有块级作用域,只有函数作用域。函数中的参数和变量在函数外部不可见,但在函数中的任何位置定义的变量在该函数中的任何地方都是可见的。
现代语言都推荐尽可能的推迟声明变量,但用在javascript上却不合适,因为他缺少块级作用域,最好的方法就是在函数体顶部声明函数中可能用到的所有变量。

闭包

闭包在于利用了内部函数拥有比外部函数更长的声明周期。
和以对象字面量形式去初始化 myobject不同,我们通过调用一个函数的形式去初始化myobject,该函数将返回一个对象字面量。此函数定义了一个value变量。该变量对increment和 getvalue方法总是可用的,但函数的作用域使得它对其他的程序来说是不可见的。

var myObject=function(){var value =0;return{increment:function(inc){value += typeof inc === 'number' ? inc :1;},getValue:function(){return value;}}
}();

注意最后的(),我们并没有对myObject返回一个函数,而是返回了一个对象字面量,包含两个方法,这些方法依旧保留对value访问的特权,而无法通过其他途径访问或非法更改。

理解内部函数能访问外部函数的实际变量而无须复制是很重要的

糟糕的例子

//构造一个函数,用错误的方式给一个数组中的节点设置事件处理程序
//当点击一个节点时,按照预想应该弹出一个对话框显示节点的序号
//但它总是会显示节点的数目。
var add_the_handlers=function(nodes){var i;for(i=0;i<nodes.length;i+=1){nodes[i].onclick =function(e){alert(i);}}
}

add_the_handlers 函数目的是给每个时间处理器一个唯一值(i)。它未能达到目的是因为事件处理器函数绑定了变量i,而不是函数在构造时的变量i的值。他会直接访问到实际变量而不是复制值。
因此我们需要做的就是在构造时传入的是复制值,通过在构造时运行一个函数,传入参数i,此时就成为了复制值。

var add_the_handlers =function(nodes){var i;for(i=0;i<nodes.length;i+=1){nodes[i].onclick=function(i){return function(e){alert(i);}}(i);}
}

现在,我们定义了一个函数并立即传递i进去执行,而不是把一个函数赋值给 onclick。那个函数将返回一个事件处理器函数。
这个事件处理器函数绑定的是传递进去的i的值,而不是定义在 add_the_handlers 函数里的i的值。那个被返回的函数被赋值给 onclick。

回调

函数可以让不连续事件的处理变得更容易。例如在像服务器发送请求时,同步很容易造成响应性降低,导致客户端进入假死状态。更好的方式是发起异步请求,提供一个当服务器的响应到达时将被调用的回调函数,异步的函数立即返回。

模块

可以使用函数和闭包来构造模块,模块是一个提供接口但是会隐藏状态和实现的函数或对象。通过使用函数去产生模块,几乎可以摒弃全局变量的使用。
模块函数利用了函数作用域和闭包来创建绑定对象与私有成员的关联。
模块模式的一般形式:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把他们保存到一个可以访问到的地方。
使用模块模式可以摒弃全局变量的使用。它促进了信息隐藏和其他优秀的设计实践。对于应用程序的封装,或者构造其他单例对象,模块模式非常有效。
模块模式也可以用来产生安全的对象。

String.method('deentityify',function(){//字符实体表。它映射字符实体的名字到对应的字符。var entity={quot:'"',lt:'<',gt:'>'};// 返回 deentityify 方法。return function(){//这才是 deentityify 方法。它调用字符串的 replace 方法,// 查找'&'开头和';"结束的子字符串。如果这些字符可以在字符实体表中找到//那么就将该字符实体替换为映射表中的值。它用到了一个正则表达式(参见第7章)。return this.replace(/&([^&;]+);/g,function(a,b){var r=entity[b];return typeof r==='string"?r:a;});}
}());

请注意最后一行。我们用()运算法立刻调用我们刚刚构造出来的函数。这个调用所创建并返回的函数才是 deentityify方法。

document.writeln('slt;&quot;&gt;'.deentityify());//<">

模块模式利用了函数作用域和闭包来创建绑定对象与私有成员的关联,在这个例子中,只有 deentityify方法有权访问字符实体表这个数据对象。
在函数中的this此时指向调用它的字符串,为方法调用模式。

级联

对于一些没有定义返回值的方法,我们可以让其返回this而不是undefined,就可以启用级联

getElement('myBoxDiv")
.move(350,150)
.width(100)
.height(100)
.color('red')
.border('10px outset')
.padding('4px')
.appendText("Please stand by").

级联可以产生具备很强表现力的接口。它也能帮助控制那种构造试图一次做太多事情的接口的趋势。

套用

套用允许我们将函数与传递给它的参数相结合去产生出一个新的函数

记忆

函数可以用对象去记住之前的操作,从而可以避免无谓的计算。这种优化被称为记忆。
比如计算斐波那契数列,将计算的结果存下来,后续再需要这个值时直接拿出来即可,无须重复计算
我们还可以将这种形式一般化,编写一个函数来帮助我们构造带记忆功能的函数。

var memoizer=function (memo,fundamental){var shell=function(n){var result=memo[n];if(typeof result!=='number'){result =fundamental(shell,n)memo(n]= result;}return result;}
};
return shell;

现在,我们可以使用memoizer来定义fibonacci数,提供其初始的memo数组和fundamental函数:

var fibonacci=memoizer([0,1],function(shell,n){return shell(n-1)+shell(n-2);
});

通过设计能产生出其他函数的函数,可以极大减少我们必须要做的工作。例如:要产生一个可记忆的阶乘函数,我们只须提供基本的阶乘公式即可:

var factorial =memoizer([1,1],function(shell,n){return n*shell(n-1);
});

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

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

相关文章

前端--第一个前端程序

第一个前端程序 第一步&#xff1a; 使用记事本&#xff0c;编写代码 在你的一个磁盘里面创建一个文件夹&#xff0c;名为前端&#xff0c;然后在里面新建一个记事本&#xff0c;在里面写如下代码&#xff0c;注意一定要使用英文&#xff0c;然后把后缀名称改为.html。 第二…

你明白C++中的多态吗?(暑假提升-多态专题)

内不欺己&#xff0c;外不欺人。———孔子 有趣的多态 1、前言2、概念3、多态定义与产生条件4、多态的重要组成成员-(虚函数)5、虚函数的重写(覆盖)6、辅助关键字override与final(了解即可)7、重载&#xff0c;重定义(隐藏)&#xff0c;重写(覆盖)8、抽象类9、多态的原理9、1、…

PHP老照片修复文字识别图像去雾一键抠图微信小程序源码

&#x1f50d;解锁复古魅力&#xff0c;微信小程序黑科技大揭秘&#xff01;老照片修复&更多神奇功能等你来试&#xff01; &#x1f4f8; 【老照片修复&#xff0c;时光倒流的美颜术】 你是否珍藏着一堆泛黄的老照片&#xff0c;却因岁月侵蚀而模糊不清&#xff1f;现在…

实验02 黑盒测试(组合测试、场景法)

1. 组合测试用例设计技术 指出等价类划分法和边界值分析法通常假设输入变量相互独立&#xff0c;但实际情况中变量间可能存在关联。全面测试&#xff1a;覆盖所有输入变量的所有可能组合&#xff0c;测试用例数量随输入变量的增加而指数增长。 全面测试需要对所有输入的各个取…

2008年上半年软件设计师【上午题】真题及答案

文章目录 2008年上半年软件设计师上午题--真题2008年上半年软件设计师上午题--答案 2008年上半年软件设计师上午题–真题 2008年上半年软件设计师上午题–答案

按模版批量生成定制合同

提出问题 一个仪器设备采购公司&#xff0c;商品合同采购需要按模版生成的固定的文件&#xff0c;模板是固定的&#xff0c;只是每次需要替换信息&#xff0c;然后打印出来寄给客户。 传统方法 如果手工来做这个事情&#xff0c;准备好数据之后&#xff0c;需要从Excel表格中…

Qt5 Ubuntu18 QStackedWidget

1、在实际项目开发过程遇到&#xff0c;如果通过UI插件的属性设置&#xff0c;通过对默认的两个页面进行提升需要切换操作的对象&#xff0c;如果该对象需要外部接口传入数据&#xff0c;实现界面信息的实时刷新&#xff0c;这样会失败&#xff0c;失败的原因很好理解&#xff…

Ubuntu安装Pytorch3d

查看对应版本的pytorch3d https://anaconda.org/pytorch3d/pytorch3d/files?page2下载后保存到服务器上安装 conda install pytorch3d-0.7.7-py310_cu118_pyt210.tar.bz2检查是否安装成功 python -c "from pytorch3d.io import load_ply" # Check for pytorch3d i…

高效应对网络攻击,威胁检测响应(XDR)平台如何提升企业应急响应能力

在数字化时代&#xff0c;企业面临的网络攻击威胁持续增加&#xff0c;如恶意软件、勒索软件、钓鱼攻击、DDoS攻击等。这些威胁不仅危及企业数据安全、系统稳定&#xff0c;还损害了品牌形象和市场信任。随着云计算、大数据、物联网的广泛应用&#xff0c;企业网络攻击面扩大&a…

简单分享下prettytable--快速制作表格

一、安装&#xff1a; pip install prettytable 二、实例&#xff1a; from prettytable import PrettyTabletable PrettyTable()table.field_names ["学号", "姓名", "语文", "数学", "英语", "物理", "化…

MAVLink代码生成-C#

一. 准备Windows下安装环境 Python 3.3 – 官网链接下载Python future模块 –pip3 install future TkInter (GUI 工具). – python for Windows自带&#xff0c;无需下载环境变量PYTHONPATH必须包含mavlink存储库的目录路径。 –set PYTHONPATH你的mavlink源码路径 源码下载在…

【昆工主办|7月昆明】第三届绿色建筑、土木工程与智慧城市国际会议(GBCESC 2024)

随着全球城市化进程的加速&#xff0c;绿色建筑、土木工程与智慧城市等议题逐渐成为了行业内外关注的焦点。在这一背景下&#xff0c;第三届绿色建筑、土木工程与智慧城市国际会议&#xff08;GBCESC 2024&#xff09;的召开&#xff0c;无疑将为相关领域的研究者、学者及从业者…

原理和组成

能力要素&#xff1a;&#xff08;1&#xff09;人员要素&#xff1a;“正确选人”。&#xff08;2&#xff09;过程要素&#xff1a;“正确做事”。&#xff08;3&#xff09;技术要素&#xff1a;“高效做事”。&#xff08;4&#xff09;资源要素&#xff1a;“保障做事”。…

【ARM】MDK-解决Flexnet服务的error:-13.66

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 记录MDK网络版部署服务器error &#xff1a;-13.66的问题解决方案&#xff0c;后续有相关发现持续更新。 2、 问题场景 在客户的服务器上部署Flexnet服务&#xff0c;将license文件检查过后&#xff0c;确认MAC地址…

如何才能半个月学习完自动化测试,成功跳槽涨薪?

一直想写类似的文章&#xff0c;但是一直没有时间&#xff0c;正好乘此周末&#xff0c;终于可以写写我的见解了<关于如何学习自动化测试>&#xff0c;其实自动化测试的知识很宽泛&#xff0c;具体细分又可以分为&#xff1a;Web自动化测试&#xff08;PythonSelenium&am…

全新UI自助图文打印系统小程序源码 PHP后端 附教程

最新自助图文打印系统和证件照云打印小程序源码PHP后端&#xff0c;为用户用户自助打印的服务&#xff0c;包括但不限于文档、图片、表格等多种格式的文件。此外&#xff0c;它们还提供了诸如美颜、换装、文档打印等功能&#xff0c;以及后台管理系统&#xff0c;方便管理员对打…

QianfanLLMEndpoint和QianfanChatEndpoint的区别

一、功能定位 QianfanLLMEndpoint&#xff1a; 功能定位&#xff1a;专注于提供基础的文本补全能力&#xff0c;主要适用于需要纯文本输出的场景。输入/输出&#xff1a;接受字符串提示作为输入&#xff0c;并返回字符串形式的补全结果。应用场景&#xff1a;适用于文本生成、…

【密码学】公钥密码的基本概念

在先前我写的密码学体制文章中谈到&#xff0c;现代密码学分为两大体制&#xff0c;介绍了一些有关对称密码体制诸如流密码和分组密码的内容。本文的主要内容则切换到公钥密码体制&#xff08;又称非对称密码体制&#xff09;&#xff0c;简述了公钥密码体制的基本思想和应用方…

六个Python实用技巧,大幅提升你的编程效率!

关注星标&#xff0c;每天学习Python新技能 Python作为当下最流行的编程语言之一&#xff0c;以其简洁易懂的语法和强大的第三方库支持&#xff0c;深受开发者的喜爱。在实际开发中&#xff0c;掌握一些实用的Python小技巧&#xff0c;可以大大提升编程效率和代码质量。 本文…

网络安全筑基篇——XSS、XML、XXE

目录 前言 什么是XSS&#xff1f; XSS的类型有哪些&#xff1f; 反射型 存储型 DOM型 XSS原理 XSS修复建议 绕过 XSS与CSRF与SSRF漏洞区别 XML是什么&#xff1f; XXE是什么&#xff1f; XXE漏洞的触发点 XXE的原理 XXE的危害有哪些&#xff1f; 1、敏感数据泄露…