javascript教程问题

本文由&– 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的!
JavaScript在IT领域中的需求量非常巨大。如果你非常精通JavaScript,你会有很多换工作、涨薪水的机会。但是在一家公司录用你之前,你必须顺利通过面试,证明你的技能。在本文中,我将向您展示5个关于JavaScript相关的问题,可以全面地测试面试者JavaScript技能和解决问题的能力。一起来看看这5个经典的JavaScript面试问题。
问题1:Scope作用范围
考虑下面的JavaScript代码:
(function() {
var a = b = 5;
console.log(b);
会输出什么样的结果?
上面的代码会打印 5。
这个问题的诀窍是,这里有两个变量声明,但 a 使用关键字var声明的。代表它是一个函数的局部变量。与此相反,b 变成了全局变量。
这个问题的另一个诀窍是,它没有使用严格模式 (‘use strict’;) 。如果启用了严格模式,代码就会引发ReferenceError的错误:B没有定义(b is not defined)。请记住,严格模式,则需要明确指定,才能实现全局变量声明。比如,你应该写:
(function() {
'use strict';
var a = window.b = 5;
console.log(b);
问题2:创建“原生”(native)方法
给字符串对象定义一个repeatify功能。当传入一个整数n时,它会返回重复n次字符串的结果。例如:
console.log('hello'.repeatify(3));
应打印 hellohellohello。
一个可能的实现如下所示:
String.prototype.repeatify = String.prototype.repeatify || function(times) {
var str = '';
for (var i = 0; i & i++) {
这个问题测试的是开发者有关JavaScript继承和prototype的知识点。这也验证了开发者是否知道该如果扩展内置对象(尽管这不应该做的)。
这里的另一个要点是,你要知道如何不覆盖可能已经定义的功能。通过测试一下该功能定义之前并不存在:
String.prototype.repeatify = String.prototype.repeatify || function(times) {/* code here */};
当你被要求做好JavaScript函数兼容时这种技术特别有用。
问题3:声明提升(Hoisting)
执行这段代码,输出什么结果。
function test() {
console.log(a);
console.log(foo());
var a = 1;
function foo() {
这段代码的结果是 undefined 和 2。
原因是,变量和函数的声明都被提前了(移到了函数的顶部),但变量不分配任何值。因此,在打印变量的时候,它在函数中存在(它被声明了),但它仍然是 undefined 。表示换句话说,上面的代码等同于以下内容:
function test() {
function foo() {
console.log(a);
console.log(foo());
问题4:this在JavaScript中如何工作的
下面的代码会输出什么结果?给出你的答案。
var fullname = 'John Doe';
var obj = {
fullname: 'Colin Ihrig',
fullname: 'Aurelio De Rosa',
getFullname: function() {
return this.
console.log(obj.prop.getFullname());
var test = obj.prop.getF
console.log(test());
答案是Aurelio De Rosa和John Doe。原因是,在一个函数中,this的行为,取决于JavaScript函数的调用方式和定义方式,而不仅仅是看它如何被定义的。
在第一个 console.log()调用中,getFullname() 被调用作为obj.prop对象的函数。所以,上下文指的是后者,函数返回该对象的fullname。与此相反,当getFullname()被分配到test变量时,上下文指的是全局对象(window)。这是因为test是被隐式设置为全局对象的属性。出于这个原因,该函数返回window的fullname,即定义在第一行的那个值。
问题5:call() 和 apply()
现在让你解决前一个问题,使最后的console.log() 打印 Aurelio De Rosa。
该问题可以通过强制使用 call() 或者 apply() 改变函数上下文。在下面我将使用call(),但在这种情况下,apply()会输出相同的结果:
console.log(test.call(obj.prop));
在这篇文章中,我们已经讨论了用来测试JavaScript开发者的五个经典问题。面试的概念和涵盖的主题通常是非常相似的。如果你不知道的一些问题的答案,不必担心:学习和经验可以慢慢积累。
如果你有其他一些有趣的问题,不要犹豫,与我们分享。它会帮助很多开发者。
译文链接:
英文原文:
翻译作者:&– 小峰
[&转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]
在文章中找不到问题答案?您还可以
热门栏目订阅3373人阅读
JavaScript(3)
我们接下来写一个系列,关于JavaScript的学习。希望能跟大家一起来学习JavaScript,至于为什么要学JavaScript呢?我们不用JavaScript也可以编程,对,但是学了会有什么作用,我给大家百度一下:
Javascript是一种由Netscape的LiveScript发展而来的原型化继承的面向对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如Perl,遗留的速度问题,为客户提供更流畅的浏览效果。当时服务端需要对数据进行验证,由于网络速度相当缓慢,只有28.8kbps,验证步骤浪费的时间太多。于是Netscape的浏览器Navigator加入了Javascript,提供了数据验证的基本功能。 简而言之JavaScript就是一种提高用户体验的语言。
好了废话不多说,我们接下来先了解一下JavaScript的常见的问题,以便在稍后的编写代码中我们能轻而易举的实现我们的代码。这部分的内容我们主要讲以下几点:
区分大小写:
其实这部分的内容主要是为了区分咱们用别的语言来编写;咱们之前使用过的语言:vb就不区分大小写,而咱们讲的这个JavaScript就区分大小写。
JS中方法和变量都是区分大小写的,因此function myFunction{}和function&Myfunction()是不同的。
这一规则也适用于JavaScript核心对象例如:Array和Object.我们在定义一个数组的时候Array,就必须是大写开头,当你写成小写之后,你会发现array的字体颜色会发生变化。
单引号、双引号:
单引号、双引号在JS中没有特殊的区别,都可以用来创建字符串。但作为一般性规则,大多数Js开发人员喜欢使用单引号而不是双引号,但是因为XHTML规范要求所有属性值都必须使用双引号括起来。这样在JS中使用单引号,而对XHTML属性使用双引号会使混合两者代码更方便也更清晰。
单引号中可以包含双引号,反之双引号也可以包含单引号。
我们看一个例子:
在这个代码段里边,因为a是一个属性值,所以用双引号,然后我们的单引号可以包含双引号,所以整个单引号括起了的地方就是一个字符串,然后+是一个连接符,所以这一个整个部分也就连接了起来。那么我们看到第二部分的代码,由于a的旁边多了一个转移字符,要是没有这个转义字符,那么当这个代码运行到class=\的时候就结束了。我们很清楚的看到,下面这个代码明显没有上面这个代码清晰,舒服。
但是对于下面情况还是需要转义字符的:
var temp='&p class=&a&& What\'s this?';如果一个单引号里边还有单引号的话,还是需要转义字符的。
首先需要说明的是:JS中括号包含2种语义,可以是分隔符也可以是表达式。
分隔符大家非常熟悉,我们举个例子来说明:(1+3)*3等于12。
表达式是这么个类型,我们看一下:
(function(){})();这里边,function之前的一对括号作为分隔符,后面的括号表示立刻执行这个方法。举个例子吧:
&title&括号的意义&/title&
&script type=&text/javascript&&
var a='123';
function aa(){
&body onload=&aa();&&
&title&括号的意义&/title&
&script type=&text/javascript&&
var a='123';
(function (){
提前说明一下,这两个结果一样的;然后我们观察发现这两段代码的区别。第一个代码直接在onload里边就加载了;然而第二段代码是使用了括号表示立即执行的效果,而且我们干脆用匿名的效果,立即就执行了这个function。所以达到了一样的效果。还有另一种写法:
&title&括号的意义&/title&
&script type=&text/javascript&&
var a='123';
function aa(){
window.onload=
这种写法跟第一种写法其实一样,我在这里想说的是:onload什么时候开始加载呢?就是在整个页面加载全部结束之后才会调用,整个页面加载包括图片加载;有时候当图片非常多的时候,我们调用这个onload就会很慢。当然我们还有别的办法,就是先加载文字部分,然后并行调用onload,同时我们再加载其他图片。我们是可以这么做的。我们简单的提一下。
函数调用和引用:
我们刚才写的两个形式,一种是带小括号的,在body里边的。然后我们直接在window里边写的就没有小括号;这就涉及到了我们函数调用和引用的问题。我们看一下:
var foo=example();
我们看得出来,前面的一个带小括号的,小括号在这里的意思就是立即执行的意思。这个的意思就是我们调用example并且把函数的返回值付给foo。第二个就是我们把这个example的函数指针指给这个foo1。所以带括号的表示一个函数的返回值;不带括号的表示一个函数的引用
换行:我们可以使用反斜杠转义字符来连接或者使用+来连接。
分号、大括号可选:
JS中每行语句的最后并不是必须用分号结尾。因此我们看:
是没有区别的。
但是对于一下代码,如:
不会翻译为
而是翻译成:
JS中不支持重载因此这里所说的重载其实更类似于替换。如:
function myFunction(a,b){}
function myFunction(a){}
由于没有重载所以上面的声明将导致下面的myFunction函数覆盖上面的函数。如:
&title&重载&/title&
&script type=&text/javascript&&
var a='123';
function aa(){
function aa(){
alert(&这是第二个&);
window.onload=
我们看到的结果是:
其实就是替换了第一个了。还有一点需要注意的是,JavaScript里边调用只与function的名字有关,与function的参数无关。如:
&title&重载&/title&
&script type=&text/javascript&&
var a='123';
function aa(){
function aa(x,y,z){
alert(&这是第二个&);
window.onload=
显示的结果还是和刚才一样。
当我们写JavaScript的方法的时候,一定要注意,名字不要使用保留字,除非你说我一定要覆盖他的保留方法,否则将导致你的代码覆盖JS核心函数:如:
&title&重载&/title&
&script type=&text/javascript&&
var a='123';
function aa(){
function aa(x,y,z){
alert(&这是第二个&);
function alert(){}
window.onload=
则将什么也没显示,但是也不报错。
作用域、闭包:
作用域指对某一属性或方法具有访问权限的代码空间。
function myFunction(){
&&&& var temp=&abc&;
上面的temp在函数外面无法访问。
闭包是与作用域相关的一个概念,它指的是内部函数即使在外部函数执行完成并终止后仍然可以访问其外部函数的属性。如:
&title&闭包&/title&
&script type=&text/javascript&&
function newaa(){
for(var i=1;i&=3;i++){
var anchor=document.getElementById(&anchor&+i);
anchor.onclick=function(){
alert(&My anchor is anchor&+i);
window.onload=
&/head&http://write.blog.csdn.net/postedit/8200870
&a id=&anchor1& href=&#&&abc&/a&&br /&
&a id=&anchor2& href=&#&&abc&/a&&br /&
&a id=&anchor3& href=&#&&abc&/a&&br /&
&/html&我们看一下效果:
无论我们点击那个abc都会出现这种情况,这是为什么?我们看一下,当我们点击事件发生后,我们的onload事件已经加载完了,所以i就变成了4了。所以就造成了这种情况。我们看一下应该如何来解决这个问题呢:
&title&闭包&/title&
&script type=&text/javascript&&
function newaa(){
for(var i=1;i&=3;i++){
var anchor=document.getElementById(&anchor&+i);
registerListener(anchor,i);
function registerListener(anchor,i){
anchor.onclick=function(){
alert(&My anchor is anchor&+i);
window.onload=
&a id=&anchor1& href=&#&&abc&/a&&br /&
&a id=&anchor2& href=&#&&abc&/a&&br /&
&a id=&anchor3& href=&#&&abc&/a&&br /&
&/html&&&& 这样我们就能达到我们想要的结果了。这就是闭包的一个作用。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:675568次
积分:5939
积分:5939
排名:第2960名
原创:62篇
评论:1654条
阅读:23226
文章:11篇
阅读:95919
文章:11篇
阅读:88396
(9)(5)(6)(4)(4)(3)(4)(8)(4)(8)(7)(4)(1)javascript问题?_百度知道javascript有时不执行的问题!
[问题点数:20分]
javascript有时不执行的问题!
[问题点数:20分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2013年10月 Web 开发大版内专家分月排行榜第三
2008年2月 总版技术专家分月排行榜第一2008年1月 总版技术专家分月排行榜第一
2008年7月 总版技术专家分月排行榜第三2008年6月 总版技术专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。1)&&& 引子
  前不久我建立的技术群里一位MM问了一个这样的问题,她贴出的代码如下所示:
var a = 1;
function hehe()
window.alert(a);
var a = 2;
window.alert(a);
  执行结果如下所示:
  第一个alert:
  第二个alert:
  这是一个令人诧异的结果,为什么第一个弹出框显示的是undefined,而不是1呢?这种疑惑的原理我描述如下:
  一个页面里直接定义在script标签下的变量是全局变量即属于window对象的变量,按照javascript作用域链的原理,当一个变量在当前作用域下找不到该变量的定义,那么javascript引擎就会沿着作用域链往上找直到在全局作用域里查找,按上面的代码所示,虽然函数内部重新定义了变量的值,但是内部定义之前函数使用了该变量,那么按照作用域链的原理在函数内部变量定义之前使用该变量,javascript引擎应该会在全局作用域里找到变量定义,而实际情况却是变量未定义,这到底是怎么回事呢?
  当时群里很多人都给出了问题的解答,我也给出了我自己的解答,其实这个问题很久之前我的确研究过,但是刚被问起了我居然还是有个卡壳期,在加上最近研究javascriptMVC的写法,发现自己读代码时候对new 、prototype、apply以及call的用法任然要体味半天,所以我觉得有必要对javascript基础语法里比较难理解的问题做个梳理,其实写博客的一个很大的好处就是写出来的知识逻辑会比你在脑子里反复梳理的逻辑映像更加的深刻。
& & & &下面开始本文的主要内容,我会从基础知识一步步讲起。
2)&&& Javascript的变量
  Java语言里有一句很经典的话:在java的世界里,一切皆是对象。
  Javascript虽然跟java没有半点毛关系,但是很多会使用javascript的朋友同样认为:在javascript的世界里,一切也皆是对象。
  其实javascript语言和java语言一样变量是分为两种类型:基本数据类型和引用类型。
  基本类型是指:Undefined、Null、Boolean、Number和String;而引用类型是指多个指构成的对象,所以javascript的对象指的是引用类型。在java里能说一切是对象,是因为java语言里对所有基本类型都做了对象封装,而这点在javascript语言里也是一样的,所以提在javascript世界里一切皆为对象也不为过。
  但是实际开发里如果我们对基本类型和引用类型的区别不是很清晰,就会碰到我们很多不能理解的问题,下面我们来看看下面的代码:
var str = "sharpxiajun";
str.attr01 = "hello world";
console.log(str);//
运行结果:sharpxiajun
console.log(str.attr01);// 运行结果:undefined
  运行之,我们发现作为基本数据类型,我们没法为这个变量添加属性,当然方法也同样不可以,例如下面的代码:
str.ftn = function(){
console.log("str ftn");
str.ftn();
  运行之,结果如下图所示:
 当我们使用引用类型时候,结果就和上面完全不同了,大家请看下面的代码:
var obj1 = new Object();
obj1.name = "obj1 name";
console.log(obj1.name);// 运行结果:obj1 name
  javascript里的基本类型和引用类型的区别和其他语言类似,这是一个老调长谈的问题,但是在现实中很多人都理解它,但是却很难应用它去理解问题。
  &Javascript里的基本变量是存放在栈区的(栈区指内存里的栈内存),它的存储结构如下图所示:
&  javascript里引用变量的存储就比基本类型存储要复杂多,引用类型的存储需要内存的栈区和堆区(堆区是指内存里的堆内存)共同完成,如下图所示:
  在javascript里变量的存储包含三个部分:
    部分一:栈区的变量标示符;
    部分二:栈区变量的值;
    部分三:堆区存储的对象。
  变量不同的定义,这三个部分也会随之发生变化,下面我来列举一些典型的场景:
  场景一:如下代码所示:
console.log(qqq);// 运行结果:undefined
  运行结果是undefined,上面的代码的标准解释就是变量被命名了,但是还未初始化,此时在变量存储的内存里只拥有栈区的变量标示符而没有栈区的变量值,当然更没有堆区存储的对象。
  场景二:如下代码所示:
console.log(qqq);// 运行结果:undefined
console.log(xxx);
  运行之,结果如下图所示:
  会提示变量未定义。在任何语言里变量未定义就使用都是违法的,我们看到javascript里也是如此,但是我们做javascript开发时候,经常有人会说变量未定义也是可以使用,怎么我的例子里却不能使用了?那么我们看看下面的代码:
xxx = "outer xxx";
console.log(xxx);// 运行结果:outer xxx
function testFtn(){
sss = "inner sss";
console.log(sss);// 运行结果:outer sss
testFtn();
console.log(sss);//运行结果:outer sss
console.log(window.sss);//运行结果:outer sss
  在javascript定义变量需要使用var关键字,但是javascript可以不使用var预先定义好变量,在javascript我们可以直接赋值给没有被var定义的变量,不过此时你这么操作变量,不管这个操作是在全局作用域里还是在局部作用域里,变量最终都是属于window对象,我们看看window对象的结构,如下图所示:
  由这两个场景我们可以知道在javascript里的变量不能正常使用即报出&xxx is not defined&错误(这个错误下,后续的javascript代码将不能正常运行)只有当这个变量既没有被var定义同时也没有进行赋值操作才会发生,而只有赋值操作的变量不管这个变量在那个作用域里进行的赋值,这个变量最终都是属于全局变量即window对象。
  由上面我列举的两个场景我们来理解下引子里网友提出的问题,下面我修改一下代码,如下所示:
//var a = 1;
function hehe()
console.log(a);
var a = 2;
console.log(a);
  结果如下图所示:
  我再改下代码:
//var a = 1;
function hehe()
console.log(a);
// var a = 2;
console.log(a);
  运行之,结果如下所示:
  对比二者代码以及引子里的代码,我们发现问题的关键是var a=2所引起的。在代码一里我注释了全局变量的定义,结果和引子里代码的结果一致,这说明函数内部a变量的使用和全局环境是无关的,代码二里我注释了关键代码var a = 2,代码运行结果发生了变化,程序报错了,的确很让人困惑,困惑之处在于局部作用域里变量定义的位置在变量第一次使用之后,但是程序没有报错,这不符合javascript变量未定义既要报错的原理。
  其实这个变量任然被定义即内存存储里有了标示符,只不过没有被赋值,代码一则说明,内部变量a已经和外部环境无关,怎么回事?如果我们按照代码运行是按照顺序执行的逻辑来理解,这个代码也就没法理解。
  其实javascript里的变量和其他语言有很大的不同,javascript的变量是一个松散的类型,松散类型变量的特点是变量定义时候不需要指定变量的类型,变量在运行时候可以随便改变数据的类型,但是这种特性并不代表javascript变量没有类型,当变量类型被确定后javascript的变量也是有类型的。但是在现实中,很多程序员把javascript松散类型理解为了javascript变量是可以随意定义即你可以不用var定义,也可以使用var定义,其实在javascript语言里变量定义没有使用var,变量必须有赋值操作,只有赋值操作的变量是赋予给window,这其实是javascript语言设计者提升javascript安全性的一个做法。
  此外javascript语言的松散类型的特点以及运行时候随时更改变量类型的特点,很多程序员会认为javascript变量的定义是在运行期进行的,更有甚者有些人认为javascript代码只有运行期,其实这种理解是错误的,javascript代码在运行前还有一个过程就是:预加载,预加载的目的是要事先构造运行环境例如全局环境,函数运行环境,还要构造作用域链(关于作用域链和环境,本文后续会做详细的讲解),而环境和作用域的构造的核心内容就是指定好变量属于哪个范畴,因此在javascript语言里变量的定义是在预加载完成而非在运行时期。
  所以,引子里的代码在函数的局部作用域下变量a被重新定义了,在预加载时候a的作用域范围也就被框定了,a变量不再属于全局变量,而是属于函数作用域,只不过赋值操作是在运行期执行(这就是为什么javascript语言在运行时候会改变变量的类型,因为赋值操作是在运行期进行的),所以第一次使用a变量时候,a变量在局部作用域里没有被赋值,只有栈区的标示名称,因此结果就是undefined了。
  不过赋值操作也不是完全不对预加载产生影响,预加载时候javascript引擎会扫描所有代码,但不会运行它,当预加载扫描到了赋值操作,但是赋值操作的变量有没有被var定义,那么该变量就会被赋予全局变量即window对象。
  根据上面的内容我们还可以理解下javascript两个特别的类型:undefined和null,从javascript变量存储的三部分角度思考,当变量的值为undefined时候,那么该变量只有栈区的标示符,如果我们对undefined的变量进行赋值操作,如果值是基本类型,那么栈区的值就有值了,如果栈区是对象那么堆区会有一个对象,而栈区的值则是堆区对象的地址,如果变量值是null的话,我们很自然认为这个变量是对象,而且是个空对象,按照我前面讲到的变量存储的三部分考虑:当变量为null时候,栈区的标示符和值都会有值,堆区应该也有,只不过堆区是个空对象,这么说来null其实比undefined更耗内存了,那么我们看看下面的代码:
var ooo = null;
console.log(ooo);// 运行结果:null
console.log(ooo == undefined);// 运行结果:true
console.log(ooo == null);// 运行结果:true
console.log(ooo === undefined);// 运行结果:false
console.log(ooo === null);// 运行结果:true
  运行之,结果很震惊啊,null居然可以和undefined相等,但是使用更加精确的三等号&===&,发现二者还是有点不同,其实javascript里undefined类型源自于null即null是undefined的父类,本质上null和undefined除了名字这个马甲不同,其他都是一样的,不过要让一个变量是null时候必须使用等号&=&进行赋值了。
  当变量为undefined和null时候我们如果滥用它javascript语言可能就会报错,后续代码会无法正常运行,所以javascript开发规范里要求变量定义时候最好马上赋值,赋值好处就是我们后面不管怎么使用该变量,程序都很难因为变量未定义而报错从而终止程序的运行,例如上文里就算变量是string基本类型,在变量定义属性程序还是不会报错,这是提升程序健壮性的一个重要手段,由引子的例子我们还知道,变量定义最好放在变量所述作用域的最前端,这么做也是保证代码健壮性的一个重要手段。
  下面我们再看一段代码:
if (undefined != str && null != str && "" != str){
console.log("true");
console.log("false");
if (undefined != str && "" != str){
console.log("true");
console.log("false");
if (null != str && "" != str){
console.log("true");
console.log("false");
if (!!str){
console.log("true");
console.log("false");
if (!!str){
console.log("true");
console.log("false");
  运行之,结果都是打印出false。
  使用双等号&==&,undefined和null是一回事,所以第一个if语句的写法完全多余,增加了不少代码量,而第二种和第三种写法是等价,究其本质前三种写法本质都是一致的,但是现实中很多程序员会选用写法一,原因就是他们还没理解undefined和null的不同,第四种写法是更加完美的写法,在javascript里如果if语句的条件是undefined和null,那么if判断的结果就是false,使用!运算符if计算结果就是true了,再加一个就是false,所以这里我建议在书写javascript代码时候判断代码是否为未定义和null时候最好使用!运算符。
  代码四里我们看到当字符串被赋值了,但是赋值是个空字符串时候,if的条件判断也是false,javascript里有五种基本类型,undefined、null、boolean、Number和string,现在我们发现除了Number都可以使用!来判断if的ture和false,那么基本类型Number呢?
var num = 0;
if (!!num){
console.log("true");
console.log("false");
  运行之,结果是false。
  如果我们把num改为负数或正数,那么运行之的结果就是true了。
  这说明了一个道理:我们定义变量初始化值的时候,如果基本类型是string,我们赋值空字符串,如果基本类型是number我们赋值为0,这样使用if语句我们就可以判断该变量是否是被使用过了。
  但是当变量是对象时候,结果却不一样了,如下代码:
var obj = {};
if (!!obj){
console.log("true");
console.log("false");
  运行之,代码是true。
  所以在定义对象变量时候,初始化时候我们要给变量赋予null,这样if语句就可以判断变量是否初始化过。
  其实if加上!运算判断对象的现象还有玄机,这个玄机要等我把场景三讲完才能说清楚哦。
  场景三:复制变量的值和函数传递参数
  首先看看这个场景的代码:
var s1 = "sharpxiajun";
var s2 = s1;
console.log(s1);//// 运行结果:sharpxiajun
console.log(s2);//// 运行结果:sharpxiajun
s2 = "xtq";
console.log(s1);//// 运行结果:sharpxiajun
console.log(s2);//// 运行结果:xtq
  上面是基本类型变量的赋值,我们再看看下面的代码:
var obj1 = new Object();
obj1.name = "obj1 name";
console.log(obj1.name);// 运行结果:obj1 name
var obj2 = obj1;
console.log(obj2.name);// 运行结果:obj1 name
obj1.name = "sharpxiajun";
console.log(obj2.name);// 运行结果:sharpxiajun
  我们发现当复制的是对象,那么obj1和obj2两个对象被串联起来了,obj1变量里的属性被改变时候,obj2的属性也被修改。
  函数传递参数的本质就是外部的变量复制到函数参数的变量里,我们看看下面的代码:
function testFtn(sNm,pObj){
console.log(sNm);// 运行结果:new Name
console.log(pObj.oName);// 运行结果:new obj
sNm = "change name";
pObj.oName = "change obj";
var sNm = "new Name";
var pObj = {oName:"new obj"};
testFtn(sNm,pObj);
console.log(sNm);// 运行结果:new Name
console.log(pObj.oName);// 运行结果:change obj
  这个结果和变量赋值的结果是一致的。
  在javascript里传递参数是按值传递的。
  上面函数传参的问题是很多公司都爱面试的问题,其实很多人都不知道javascript传参的本质是怎样的,如果把上面传参的例子改的复杂点,很多朋友都会栽倒到这个面试题下。
为了说明这个问题的原理,就得把上面讲到的变量存储原理综合运用了,这里我把前文的内容再复述一遍,两张图,如下所示:
  这是基本类型存储的内存结构。
  这是引用类型存储的内存结构。
  还有个知识,如下:
  在javascript里变量的存储包含三个部分:
  部分一:栈区的变量标示符;
  部分二:栈区变量的值;
  部分三:堆区存储的对象。
  在javascript里变量的复制(函数传参也是变量赋值)本质是传值,这个值就是栈区的值,而基本类型的内容是存放在栈区的值里,所以复制基本变量后,两个变量是独立的互不影响,但是当复制的是引用类型时候,复制操作还是复制栈区的值,但是这个时候值是堆区对象的地址,因为javascript语言是不允许操作堆内存,因此堆内存的变量并没有被复制,所以复制引用对象复制的值就是堆内存的地址,而复制双方的两个变量使用的对象是相同的,因此复制的变量其中一个修改了对象,另一个变量也会受到影响。
  原理讲完了,下面我列举一个拔高的例子,代码如下:
var ftn1 = function(){
console.log("test:ftn1");
var ftn2 = function(){
console.log("test:ftn2");
function ftn(f){
ftn(ftn1);// 运行结果:test:ftn1
console.log("====================华丽的分割线======================");
ftn1();// 运行结果:test:ftn1
  这个代码是很早之前有位朋友考我的,我当时答对了,但是我是蒙的,问我的朋友答错了,其实当时我们两个都没搞懂其中缘由,我朋友是这么分析的他认为f是函数的参数,属于函数的局部作用域,因此更改f的值,是没法改变ftn1的值,因为到了外部作用域f就失效了,但是这种解释很难说明我上文里给出的函数传参的实例,其实这个问题答案就是函数传参的原理,只不过这里加入了个混淆因素函数,在javascript函数也是对象,局部作用域里f = ftn2操作是将f在栈区的地址改为了ftn2的地址,对外部的ftn1和ftn2没有任何改变。
  记住:javascript里变量复制和函数传参都是在传递栈区的值。
  栈区的值除了变量复制起作用,它在if语句里也会起到作用,当栈区的值为undefined、null、&&(空字符串)、0、false时候,if的条件判断则是为false,我们可以通过!运算符计算,因此当我们的代码如下:
var obj = {};
if (!!obj){
console.log("true");
console.log("false");
  结果则是true,因为var obj = {}相当于var obj = new Object(),虽然对象里没什么内容,但是在堆区里,对象的内存已经分配了,而变量栈区的值已经是内存地址了,所以if语句判断就是true了。
  看来本主题又没法写完,其实本来我写本文是想讲new,prototype,call(apply)以及this,没想讲变量定义就讲了这么多,算了,先发表出来吧,吃了晚饭接着写,希望今天写完。
阅读(...) 评论()}

我要回帖

更多关于 javascript教程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信