JavaScript最详细基础语法总结(跳坑记录!)
众所周知,JavaScript是一种弱类型的语言。博主在学习JS前,只接触过C语言和C++,所以在一开始学习时,思维难免转换不过来。而当学习深入后,博主逐渐明白了其精妙所在,故在此做一个总结,如果同时可以对大家有一点学习上的帮助,就再好不过了。
在开始之前,我要先给大家灌输(?)一个概念:在JavaScript中,一切皆为对象!(Everything is an object) ,这一点学过Java的同学可能很好理解,如果你现在还不太明白,也没关系,跟着我往下看吧,你总会明白的!
全局与块
JS是一种弱类型的语言,它是可以在运行中动态生成类(不仅是对象!)的,也就是说,它不需要提前指定变量的类型,这与我们之前学习的强类型语言C++、JAVA等有很大不同。JS中可以用var或let来声明变量,它们有什么区别呢?
我们先来看一段代码:
var a = "abcdef";
var b = a;b += "ghi";
console.log("abcdefghi".length);
console.log(a, b);var c = {x: 123,y: "456"
};var d = c;
d.y = "789";
console.log(c, d);var e = [1, 2, 3, 4];
var f = e;
f.pop();
console.log(e, f);
已安装node.js的同学可以尝试在终端运行,如果没有安装,也没有关系,可以将代码包装入HTML里在网页中运行,看看结果。
跟你想的一样吗?我们一个个来说:
var a = "abcdef"; var b = a;b += "ghi"; console.log("abcdefghi".length); console.log(a, b);
对于常量,这里的 var b =a 相当于var b = "abcdef" ,也就是说,a和b是两个对象,那么下面改变b的操作,对a是没有影响的。
var c = {x: 123,y: "456" };var d = c; d.y = "789"; console.log(c, d);
这里我们先声明并初始化了一个对象,那么, var d = c 这段代码表示什么呢?是C++里的复制构造函数吗?不是的,在这里,是对d的一个赋值语句!简单来说,就是c和d其实都是一个引用,它们指向了同一个对象!那么自然,对d所指向的对象的改变,也就是对c所指向的对象的改变。
var e = [1, 2, 3, 4]; var f = e; f.pop(); console.log(e, f);
同样的,只不过这里定义的是一个数组而已。不过,数组也是个对象,毕竟在JS里,一切皆为对象嘛。
总结一下,在JS里,对象名(如d,c,e,f)是一个引用,它们之间的赋值,实现的是别名绑定,而不是备份。
好好观察一下我们上面定义的
var c = {x: 123,y: "456" };
它其实是一个键值对的格式,即JSON(JavaScript Object Notation)格式。
let则是ES6(ECMAScript 6.0,JavaScript 语言的下一代标准。不太了解的同学,这里推荐阮一峰老师的教程)中新增的命令,用来指示块级作用域中的变量(即用let命令声明的变量仅在其所在的代码块内有效)。
什么意思呢?再来看一段代码:
{var a = 1;let b = 2;
}console.log(a, b);
猜猜会输出什么?
哈哈,高手可能不屑一笑:当然是报错啦!——
现在理解“用let命令声明的变量仅在其所在的代码块内有效”这句话了吗?没错,b 是let定义在上面的花括号里的(一个块作用域),出了这个作用域,还想调用console.log输出它,当然是 undefined 了!
常量
来看一段代码:
const a = {};a.x = 123;
a.y = "456";
console.log(a);a = {a:1,b:2}; // Assignment to constant variable.
这里我们要讲下,个人理解,在JS里,const a = 2 就类似于C++里的 int * const a 即a是指向变量的常量指针。(注意与const int *a,变量指针指向常量 的区别),即当你声明 a 是 "const" 的,又把它指向一个对象(一切皆对象,还记得吗?),那么它的指向是不能改变的!
闭包
什么是闭包?对这个概念模模糊糊的同学,可以先去看看这个博主的文章,个人感觉讲得还是比较有趣、易于理解的,他认为,有权访问另一个函数作用域内变量的函数都是闭包。
然后我们来看一段代码:
var a = [];for (var i = 0; i < 10; ++i)a[i] = function () { console.log(i); }a[0]();
a[5]();
a[9]();
你觉得它会输出啥?0,5,9对不对!嗨呀,这就犯了跟博主初学时一样的错误啦~
那么它会输出什么呢?我们先看看结果再来吹(?):
当当!有没有幡然醒悟?没有?ok,那就往下看——
在这里,a其实是一个闭包类型(函数),而 i 是用 var 定义的全局变量! 所以在结束循环后,根据判定条件,i 的值应该是10。
那么当我们用a[0]()等来调用函数时,console.log(i) 输出的其实都是10。
其实关键就是,这里的 i 存储的是“引用”而不是值,所以其“值”只有在执行时才能确定,而 i 是全局变量,当执行时,它早已自增到10!
如果想得到你想要的结果,即a[0]()输出0,a[5]()输出5等,只需把 var i = 0 中的 var 改为 let 即可!
循环
比较简单,基本只有记忆的部分,直接看代码吧:
var a = [1, 2, 3, 4, 5];//traditional
for (var i = 0; i < 5; ++i) console.log(a[i]);//foreach: in -- i is index
for (var i in a) console.log(a[i]);//foreach: of -- i is element
for (let i of a) console.log(i);
第一种是我们传统使用的遍历方法,不再赘述;
第二种,用的是 in ,所以 i 是数组的下标;
第三种,用的是 of,所以 i 就是数组里每个对象。
数组(解构)
首先,在JS里,定义数组有三种方式:
var a=[];
var b=[1,2,3];
var c=new Array();
然后我们来看一段代码吧:
let a = [1, 2, 3];console.log(a);let [x, y, z] = a;
console.log(x, y, z);[x, z] = [z, x];
console.log(x, z);
- 这里, let[x,y,z] = a 其实是数组的解构。 因为它把数组里的三个值分别赋给x,y,z三个值。
- [x,z] = [z,x] 的作用其实是借助数组交换x和z的值。想想在C++里,我们要交换两个数的值,至少需要三行代码,如果涉及到函数传值,就更多了。而这里,我们只需要一行代码,就可以交换两个数的值。
字符串
常用函数
let s = "this is a sample";console.log(s);
console.log(s.includes("sample"));
console.log(s.startsWith("this"));
console.log(s.endsWith("le"));
JS提供的这些函数对coder非常友好。(省多少事啊!)
Template String
(这里用了英文表示,因为我不知道该怎么翻译合适。。)
let v = 123;
let t = `value = ${v};`;console.log(t);console.log(t.repeat(3));
在想输出语句 + 变量值时,我们常常会采用这样的方式:let t = 'value = '+v+";" , 即字符串拼接,这样固然没有语法错误,但似乎过于麻烦。
而看看我们上面代码中采用的反引号的方式,并用 $ 引用变量的值,使得常量和变量可以在一个(反)引号里输出,简便多了。
其实,JS的这个语法借鉴了Linux操作系统的命令。有用过Linux操作系统的同学肯定不陌生批量操作文件的命令。比如,有一个文件在多个文件夹里都有,现在我发现这个文件写漏了一点,要在文件最后加一行 “hello”才正确,怎么办?难道要一个一个文件夹打开去修改吗?no,这也太不优雅了!其实,可以用类似 $ sed -i '$a\hello' `find-name...` 的命令(注意,前面是单引号,表示插入一行"hello",后面是反引号,表示找到所有名字为...的文件),这就实现了批量处理。
正则表达式
var REG = /(\d{4})-(\d{2})-(\d{2})/;var s = "2017-11-28";
var res = REG.exec(s);
// 得到一个数组,0为原字符串,123分别为3个子模式
// 数组解构
var [oldstr, year, month, day] = res;
console.log(year, month, day);
正则表达式,多么伟大的发明!它描述了一种字符串匹配的模式,可以用来检查一个字符串是否含有某种子串、将匹配的子串做替换或者从某个字符串中取出符合某个条件的子串等等。在它未出现之前,字符串匹配对于coder来说是一件多么痛苦的事情!好在,它出现了,而我们只需学好它就行了——
由于对之前完全没接触过这个概念的同学来说,理解会有点困难(比如一开始的博主),所以我们一个字符一个字符分析讲解!
var REG = /(\d{4}) - (\d{2}) - (\d{2})/
- 两边的 / ,指定正则表达式的范围
- () 括号,包起分组
- \d ,表示转义(10进制)
- {4} ,表示4位数
- - ,表示分隔符
用正则表达式处理字符串后,得到一个数组,下标为0的值为原字符串,下标为1、2、3的值分别为3个子模式。
经过数组解构后,我们分别得到了三个值。
函数
缺省值
在JS中,函数可以被当作对象返回(与C++不同);而且,JS里也没有函数重载的概念,毕竟是弱类型嘛。
function show(x, y)
{x = x || 0;y = y || "default";console.log(x, y);
}show(1, "hello");
show(2);
show();
这里的 x= x || 0 和 y = y || "default" 表示,如果x有定义则x=x,否则为右边的值。
这里我们就顺带讲一下 null 和 undefined 吧。
个人理解,null 表示什么都没有(类似C++里的void),用数学的思想来说,就是没有一个集合;
而 undefined 表示有一个集合,只不过这个集合里没有东西!
所以,如果用 show(null) 调用函数,表示 x 的值为null,y 的值为undefined。
上面代码里的写法有人就不爽,比如我。为啥要用 x= x || 0 这种表意不清的写法呢?所以,ES6推出了缺省值的方法——
function show(x = 0, y = "default")
{console.log(x, y);
}show(1, "hello");
show(2);
show();
作用跟上面是一样的。
这里提一下,虽然现在几乎所有主流浏览器都支持了ES6的语法,但如果你想适配ES5去降级,可以用Babel。
不定参数
JS支持不定参数的用法。
function add(...numbers)
{var sum = 0;for (let e of numbers) sum += e;return sum;
}// 完成最后字符串的连接
console.log(add(1, 2, 3, 4,"abc"));
结果:
箭头函数
箭头函数有点类似C++里的lambda函数,其实就是函数简写。
var f = function (x)
{return x * 2;
}console.log(f(10));var g = x => x * 2;
console.log(g(10));// map:映射,数组[1,2,3]的每个值都要改变
console.log([1, 2, 3].map(x => x * x));// ()扩起,返回一个对象。其函数体只有一条赋值语句{a: x, b: y}
var h = (x, y) => ({a: x, b: y});
console.log(h(5, 6));
尾调用
尾调用:调用函数f是函数g的最后一个操作(return)。
function f(x)
{return x * 2;
}// 尾调用
function g(x)
{if (x < 0)return f(-x);return f(x);
}console.log(g(-2));
console.log(g(2));// 不是尾调用
function g1(x)
{let y = f(x);return y;
}// 不是尾调用
function g2(x)
{return f(x) + 1;
}// 不是尾调用。没有return语句,而每个函数最后都会有一个默认的return语句(只是这里省略)
function g3(x)
{f(x);
}
尾调用有很大的缺点。因为调用帧(堆栈)的,时间复杂度为O(n)。
尾调用是可以被优化的。如上面的g1,将中间结果参数化,只占用1帧,时间复杂度为O(1)。
这里用斐波拉切数列的例子讲解函数递归的优化:
// 函数递归
// 耗费资源 消耗堆栈
// 优化:中间结果参数化!(func6)
function fibo(n)
{return n <= 2 ? 1 : fibo(n - 1) + fibo(n - 2);
}// 获得命令行参数
var n = parseInt(process.argv.splice(2));
console.log(fibo(n));
可以将中间结果参数化:
// func6// 递归参数化
function fibo(n, a, b)
{return n <= 1 ? a : fibo(n - 1, b, a + b);
}var n = parseInt(process.argv.splice(2));
console.log(fibo(n, 1, 1));
也可以进行柯里化:
//currying(柯里化) 参数只有一个
function fibo(n)
{let _fibo = (n, a, b) => n <= 1 ? a : _fibo(n - 1, b, a + b);return _fibo(n, 1, 1);
}//default parameters(缺省参数,ES6实现)
function fibo2(n, a = 1, b = 1)
{return n <= 1 ? a : fibo2(n - 1, b, a + b);
}var n = parseInt(process.argv.splice(2));
console.log(fibo(n));
console.log(fibo2(n));
动态添加
在JS中,可以在运行中为一个对象添加属性、方法等:
var harry = {name: "Harry Potter",age: 17,say: function () { console.log("I'm Harry Potter.");}
};console.log(harry);
harry.say();//动态添加
harry.tutor = "Pro. Dumbledore";
console.log(harry);harry.greeting = function () { console.log("Hello!"); };harry.greeting();
结果如下:
this
我们常常发现在函数中有“this”这个值,它到底代表什么呢?是这个函数吗?
对this的指向还迷迷糊糊的同学,这里推荐一篇博文,个人感觉博主写的很好。与其它博文对比之下更易理解、例子也很典型。http://www.imooc.com/article/1758
来看一个例子:
// this不是指函数本身,而是指启动这个函数的对象
function whoami()
{ console.log(`My name is ${this.name}`);
}var harry = {name: "Harry Potter",age: 17
};var ron = {name: "Ronald Wesley",who: whoami
};whoami.call(harry);
whoami.call(ron);
ron.who();harry.buddy = ron;
whoami.call(harry.buddy);
你觉得会是怎样的输出?
注意:
this不是指函数本身,而是指启动/调用这个函数的对象!
call()函数表示调用,那么答案显而易见了:
箭头函数
来看一段代码:
var harry = {name: "Harry Potter",curses: ["Stupify", "Petrify", "Riddikulus"],learn: function () {this.curses.map(function (x) { // 这里的this指harryvar msg = `I'm ${this.name}. I'm learning the curse "${x}".`; // 这里的this不指harryconsole.log(msg);return msg;});}
};harry.learn();
如我们刚才所讲的,会发现第一个this指harry,第二个则不然(非严格模式中指向window,这里严格模式下为undefined)。
为了解决这个麻烦的问题,我们既可以用 var that = this; 保留当前函数执行上下文的this,也可以用箭头函数——
var harry = {name: "Harry Potter",curses: ["Stupify", "Petrify", "Riddikulus"],learn: function () {this.curses.map((x)=>{ // 这里的this指harryvar msg = `I'm ${this.name}. I'm learning the curse "${x}".`; // 改为箭头函数,这里的this也指harryconsole.log(msg);return msg;});}
};harry.learn();
结果:
构造函数
工厂方法
function addPupil(name, age)
{var pupil = new Object;pupil.name = name;pupil.age = age;pupil.say = function (greeting) {console.log(`${greeting} I'm ${this.name}. I'm ${age}`);};return pupil;
}var harry = addPupil("Harry Potter", 16);
var ron = addPupil("Ronald Wesley", 17);harry.say("Hello!");
ron.say("Hi, dude!");
1
function pupil(name, age)
{this.name = name;this.age = age;this.say = function (greeting) {console.log(`${greeting} I'm ${this.name}. I'm ${this.age}`);};
}var harry = new pupil("Harry Potter", 16);
var ron = new pupil("Ronald Wesley", 17);harry.say("Hello!");
ron.say("Hi, dude!");
2
function say(greeting)
{console.log(`${greeting} I'm ${this.name}. I'm ${this.age}`);
}function pupil(name, age)
{this.name = name;this.age = age;this.say = say;
}var harry = new pupil("Harry Potter", 16);
var ron = new pupil("Ronald Wesley", 17);harry.say("Hello!");
ron.say("Hi, dude!");
3
function say(greeting)
{console.log(`${greeting} I'm ${this.name}. I'm ${this.age}`);
}function pupil(name, age)
{this.name = name;this.age = age;this.say = say;
}var harry = new pupil("Harry Potter", 16);
var ron = new pupil("Ronald Wesley", 17);harry.say("Hello!");
ron.say("Hi, dude!");pupil.tutor = "Prof. Dumbledore"; // undefined。要用原型添加console.log(harry.tutor, ron.tutor);
console.log(pupil);var hemione = new pupil("Hemione Granger");
console.log(hemione.tutor);
4
function say(greeting)
{console.log(`${greeting} I'm ${this.name}. I'm ${this.age}`);
}function pupil(name, age)
{this.name = name;this.age = age;this.say = say;
}var harry = new pupil("Harry Potter", 16);
var ron = new pupil("Ronald Wesley", 17);harry.say("Hello!");
ron.say("Hi, dude!");pupil.prototype.tutor = "Prof. Dumbledore";console.log(harry.tutor, ron.tutor);
console.log(pupil);var hemione = new pupil("Hemione Granger");
console.log(hemione.tutor);
继承
1
//语法糖
//用extend继承
class felid
{constructor(name = "felid"){this.name = name;}prey(){console.log(`${this.name} preys.`);}
}class tiger extends felid
{constructor(name = "tiger"){super(name);}cry(){console.log(`${this.name} roars.`);}
}class bengaltiger extends tiger
{constructor(name = "bengaltiger"){super(name);}
}b = new bengaltiger();
b.prey();
b.cry();
2
//原型链继承
function felid(name = "felid")
{this.name = name;
}
felid.prototype.prey = function () { console.log(`${this.name} preys.`);};function tiger(name = "tiger")
{this.name = name;
}
tiger.prototype = new felid("tiger");
tiger.prototype.cry = function () { console.log(`${this.name} roars.`);};t = new tiger();
t.prey();
t.cry();function bengaltiger(name = "bengaltiger")
{this.name = name;
}
bengaltiger.prototype = new tiger("bengaltiger");b = new bengaltiger();
b.prey();
b.cry();
原型:
var A = function () { console.log("Hello!"); }
var a = new A();console.log(A.prototype);
console.log(a.__proto__);
console.log(A.prototype.constructor);
console.log(a.constructor);
原型链:
var A = function () {}
var a = new A();console.log(a.__proto__.constructor);
console.log(a.__proto__.__proto__.constructor);
console.log(a.__proto__.__proto__.__proto__);
3
function felid(name = "felid")
{this.name = name;
}
felid.prototype.prey = function () { console.log(`${this.name} preys.`);};function tiger()
{//felid.call(this);
}
tiger.prototype = new felid("tiger");
tiger.prototype.cry = function () { console.log(`${this.name} roars.`);};t = new tiger;
t.prey();
t.cry();
4
function felid(name = "felid")
{this.name = name;
}
felid.prototype.prey = function () { console.log(`${this.name} preys.`);};function tiger()
{//felid.call(this);this.name = "tiger";
}
(function () {var para = function () {};para.prototype = felid.prototype;tiger.prototype = new para();tiger.prototype.cry = function () { console.log(`${this.name} roars.`);};
})();t = new tiger;
t.prey();
t.cry();
JavaScript最详细基础语法总结(跳坑记录!)相关推荐
- Javascript核心技术的基础语法
Javascript核心技术的基础语法 一.什么是javascript Javascript是基于对象和事件驱动的脚本语言,主要是嵌入到HTML中,应用在客户端,动态操作网页元素,也可以作用于服务端. ...
- JavaScript脚本语⾔基础语法笔记总结
JavaScript脚本语⾔&基础语法&笔记总结 0.学前准备: JavaScript简介使⽤⽅法 1. JavaScript简介 什么是JavaScript? 前端Web技术的组成: ...
- 【软件测试大赛Web应用省赛】跳坑记录
[软件测试大赛Web应用省赛]跳坑记录 本文写在预赛通过的基础上,默认后来者已经接触过web应用测试,并有一定的基础知识. 本文写于2020.11.8省赛结束,希望这些坑以后不会再有人跳进去. [前排 ...
- Javascript(前端知识)----------基础语法
多态的本质是:让用户不知道引用实际上是什么类型,也能调用里面的方法,让封装更进一步,降低了用户的使用成本: 举例子: 1)集合类: List<String> list=new ArrayL ...
- JavaScript进阶篇①——基础语法
一.认识JS 你知道吗,Web前端开发师需要掌握什么技术?也许你已经了解HTML标记(也称为结构),知道了CSS样式(也称为表示),会使用HTML+CSS创建一个漂亮的页面,但这还不够,它只是静态页面 ...
- 【JavaScript 笔记 】— 基础语法(数据类型、字符串、数组、对象、Map、Set、iterable、函数基础)
JavaScript个人笔记 数据类型和变量 浮点数的相等比较 null 和 undefined == 与 === strict模式 字符串 模板字符串 字符串常用方法 数组 数组常用方法 对象 条件 ...
- javascript入门及基础语法结构
1.什么是变量? 如何声明变量?变量名的命名规则 变量: 可变的量 var 变量名=变量值; 变量的组成: (1)字母 _ $ 不能以数字开头 ,(2)变量名不能是JavaScript关键字和保留字开 ...
- JavaScript高级编程语言基础语法
1.初识JavaScript 1.2 JavaScript是什么 JavaScript是世界上最流行的语言之一,是一种运行在客户端的脚本语言( Script是脚本的意思) 脚本语言∶不需要编译,运行过 ...
- GO超详细基础语法黑点
更新: 进程有自己独立的堆和栈,不共享堆和栈. 线程有自己独立的栈和共享的堆,不共享栈. 协程和线程一样共享堆,不共享栈.协程有程序员在协程的代码里显示调度. 执行协程只需要极少的栈内存(大概是4-5 ...
- python语法基础整理_Python基础语法笔记整理——只记录部分不熟少用的
重新整理python语法,不一定有样例 Reference 司守奎,孙玺青,<Python数学实验与建模> vitu.ai print()输出 #sep代表各输出之间用i隔开 print( ...
最新文章
- 剑指offer例题——二维数组中的查找
- 加强路由器的安全访问控制
- QT教程1:ubuntu18.04安装QT5
- 2020-11-28(全局变量和局部静态变量)
- JZOJ5906 传送门
- mysql查询表字段是否存在_Mysql判断表字段或索引是否存在
- mysql 多条记录判断相加减进行计算
- 苹果Mac 默认浏览器如何更改?设置成自己习惯用的浏览器
- 柏云服务器点歌系统IP,阿蛮歌霸网络版点歌系统安装说明.docx
- Linux将鼠标解放,DwellClick:让鼠标下岗 解放你的手指
- 购买域名和个人网站备案流程
- knowledge transfer
- JSON格式校验工具
- Pyecharts绘制图表大全——柱形图
- 第12周 项目4-输出从顶点u到v的所有简单路径
- 怎么提高程序的可修改性
- 阿里云服务器和腾讯云服务器哪个更好?多维度对比得出了结论
- unity 适配iPhone X底部横条
- 房产中介行业解决方案
- animation初体验