理解JavaScript中的参数传递 - leetcode189. Rotate Array
1、关于leetcode
这是第一篇关于leetcode的题解,就先扯点关于leetcode的话。
其实很早前就在博客园看到过leetcode一些题解,总以为跟一般OJ大同小异,直到最近点开了一篇博文Leetcode 编程训练,无意间点进leetcode的主页看了下,乖乖,居然能用JavaScript提交代码(还能用python、ruby等)!瞬间来了兴趣,一口气把几十道水题都切完了,代码放在了github,有兴趣的可以参考或者帮忙review一下。对于个人认为有意思的题目,楼主也会时不时地写些题解和大家分享下。
2、解题过程
今天要说的是Rotate Array这题,并不是说这题有多么地难(leetcode把它难度定位为EASY),而是让我理解了JavaScript中以前听说过但是一直没引起重视的一个很重要的性质。
先回到这道题本身,题目很简单,给一个数组,向右移动k位,求新的数组,关键来了,题目要求你Do not return anything, modify nums in-place instead.。右移k位,相当于把数组最右边的k位放到数组开头,还要考虑k大于数组长度的情况,似乎也很容易想到,写下如下代码:
var rotate = function(nums, k) {
k %= nums.length;
var tmp = [];
if (k)
tmp = nums.slice(-k);
nums.splice(-k, k);
nums = tmp.concat(nums);
};
tmp保存了右边要移动要前面的数组(slice),而nums自己则截掉后面要移动的数组(splice),然后把两段数组一拼,不是说直接修改nums数组么,那把结果直接赋给nums就ok了!但是无情地返回了wrong answer,leetcode不提供sample,但是出错了会给一组出错了的数据,数据如下:
Input: [1,2], 1
Output: [1]
Expected: [2,1]
尝试着把数组带入,打印结果:
var rotate = function(nums, k) {
k %= nums.length;
var tmp = [];
if (k)
tmp = nums.slice(-k);
nums.splice(-k, k);
nums = tmp.concat(nums);
console.log(nums); // [2, 1]
};
rotate([1, 2], 1);
靠,输出的真的是你expected的东西啊!正当我百思不得其解的时候,我突然意识到我犯了一个很严重的错误。leetcode服务器匹配你的结果正确与否,不可能进入你写的函数里去判断!而实际上,它应该是这样判断的:
var rotate = function(nums, k) {
k %= nums.length;
var tmp = [];
if (k)
tmp = nums.slice(-k);
nums.splice(-k, k);
nums = tmp.concat(nums);
};
var a = [1, 2];
rotate(a, 1);
console.log(a); // [1]
确实与expected的不符!为什么数组a会变成1?怎样才能变成expected的答案?我们接下去看。
3、JavaScript函数的参数传递方式
关于变量值的复制我们都已经很清楚了,基本类型(undefined、null、boolean、number、string)和引用类型(object)是不一样的。如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值赋值到为新变量分配的位置上,此后这两个变量可以参与任何操作而不会互相影响。而当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中,不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量:
var a = [0, 1, 2, 3];
var b = a;
b.push(4, 5, 6);
console.log(a); // [0, 1, 2, 3, 4, 5, 6]
而ECMAScript中所有函数的参数都是按值传递的,也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样!弄清楚了这一点,我们再回到上面的代码。
当没有执行nums = tmp.concat(nums);这行代码时,数组a和函数rotate的参数nums都引用着同一个地址,于是它们的值一起改变;当执行这行代码后,nums指向了一个新的地址,无论它指向哪里,此时的它已经和数组a没有任何关系,也就是说a的值在这一刻之后不会再变化了。

于是我们很清楚地知道,要想在nums上体现a的变化,函数内的nums参数不能去引用一个新的对象,只能在自身上操作,思考下写下如下代码,终于AC:
var rotate = function(nums, k) {
k %= nums.length;
var tmp = [];
if (k)
tmp = nums.slice(-k);
nums.splice(-k, k);
Array.prototype.unshift.apply(nums, tmp);
};
利用JavaScript的这个性质,能出现一些很神奇的效果,这些我也在做题过程中逐渐体会到了一点,具体题目到时再跟大家分享吧!
理解JavaScript中的参数传递 - leetcode189. Rotate Array的更多相关文章
- 深入理解JavaScript中创建对象模式的演变(原型)
深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...
- 理解javascript中的回调函数(callback)【转】
在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...
- 【拾遗】理解Javascript中的Arguments
前言 最近在看JavaScript相关的知识点,看到了老外的一本Javascript For Web Developers,遇到了一个知识盲点,觉得老外写的很明白很透彻,记录下来加深印象,下面是我摘出 ...
- 深入理解JavaScript中的作用域和上下文
介绍 JavaScript中有一个被称为作用域(Scope)的特性.虽然对于许多新手开发者来说,作用域的概念并不是很容易理解,我会尽我所能用最简单的方式来解释作用域.理解作用域将使你的代码脱颖而出,减 ...
- 理解JavaScript中的原型继承(2)
两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...
- 深入理解JavaScript中的属性和特性
深入理解JavaScript中的属性和特性 JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaSc ...
- 深入理解javascript中执行环境(作用域)与作用域链
深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...
- 【干货理解】理解javascript中实现MVC的原理
理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程 ...
- 理解javascript中的策略模式
理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点:1. 策略模式利用组合,委托等技术和思想,有效 ...
随机推荐
- Mina 快速入门
Mina是什么 Mina是一个基于NIO的网络框架,使用它编写程序时,可以专注于业务处理,而不用过于关心IO操作.不论应用程序采用什么协议(TCP.UDP)或者其它的,Mina提供了一套公用的接口,来 ...
- SQL Server 中的逻辑读与物理读
首先要理解逻辑读和物理读: 预读:用估计信息,去硬盘读取数据到缓存.预读100次,也就是估计将要从硬盘中读取了100页数据到缓存. 物理读:查询计划生成好以后,如果缓存缺少所需要的数据,让缓存再次去读 ...
- Kafka 分布式的,基于发布/订阅的消息系统
Kafka是一种分布式的,基于发布/订阅的消息系统.主要设计目标如下: 通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能. 高吞吐量:即使是非常 ...
- hadoop2.4.1集群搭建
准备Linux环境 修改主机名: $ vim /etc/sysconfig/network NETWORKING=yes HOSTNAME=hadoop001 修改IP: # vim /etc/sys ...
- x01.FileProcessor: 文件处理
姚贝娜落选,意味着好声音失败.“我们在一起”的精彩亮相,正如同她的歌声,愈唱愈高,直入云霄. 文件处理,无外乎加解密,加解压,分割合并.本着“快舟"精神,花了两天时间,写了个小程序,基本能满 ...
- linux添加开机自启动脚本示例详解
linux下(以RedHat为范本)添加开机自启动脚本有两种方法,先来简单的; 一.在/etc/rc.local中添加如果不想将脚本粘来粘去,或创建链接什么的,则:step1. 先修改好脚本,使其所有 ...
- 让HTML页面缩放适应移动客户端尺寸
多的不说了,直接看代码吧 <html lang="en"> <head> <meta http-equiv = "X-UA-Compatib ...
- 使用数据库sqlite3 C语言实现登陆注册的功能
//此代码为注册功能 void create_regtable() { int rc; //rc为返回值,判断函数是否执行成功 rc=0函数执行成功,rc !=0函数执行失败 sqlite3 *db; ...
- 深入理解C语言
语言只是一种工具,任何语言之间都是相通的,一通则百通,关键是要理解语言背后的思想,理解其思想,任何语言,拿来用就行了.语言没有好坏之分,任何语言既然存在自然有它存在的价值. 在一个到处是OOP的年代, ...
- 瘟疫公司中国版(Android)手动破解内购
前言 洒家近日下载了个瘟疫公司中国版(安卓版)(com.easymobi.plagueinc.mi ,版本 1.1.2(5)(.mi 小米版)),发现游戏需要内购而且价格不菲. 需求 root权限 文 ...