javascript toturior
一小时入门JavaScript:https://www.youtube.com/watch?v=W6NZfCO5SIk&ab_channel=ProgrammingwithMosh
JS基础语法练习题:https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/
1. JS introduction
1.1 JS intruduction
-
什么是javascript
-
javascript可以做什么
-
javascript代码运行在什么地方
-
javascript vs ECMAScript
javascript是一种流行的编程语言,可以写前端和后端代码。
曾经在很长一段时间内,JS只能在浏览器中用来创建交互式的web页面。现在则可以用来实现web/mobile应用,实时网络apps,命令行工具,游戏等。
原生的javascript代码只能运行在浏览器中,所有浏览器都有javascrript引擎。有了node之后,JS代码可以运行在node中。所以,JS代码可以运行在浏览器或者node中,浏览器和node为JS提供了运行时环境。
ECMAScript是一个specification,JS是一个编程语言。
1.2 Setup 开发环境
编辑器:vscode
node
插件:live server
1.3 JS基础
js脚本在html文件中可以放到head或body中,最佳实践是放到body的尾部。
原因1:浏览器从上到下解析HTML文件并渲染元素,如果放在head或者body的上面会导致浏览器在运行JS脚本的时候页面空白,需要在页面元素都出现之后再运行JS脚本;
原因2:JS脚本可能会使用元素,这样可以确保脚本使用的元素都已经被浏览器渲染完成。
1.4 Separation of concerns
实际上一个项目会有很多JS脚本,JS脚本不可能全部放在HTML文件中。我们创建单独的index.js文件,然后在HTML中引用js文件。
<script src="index.js"></script>
1.5 JS in node
上面都是在浏览器中运行JS脚本,现在我们在node中运行JS脚本。
node index.js
Node: a program includes google’s V8 javascript engine. a runtime environment for executing JS scripts.
2. JS basic
2.1 Variables & constant
variable
在ES6之前使用var声明变量,在ES6之后使用let声明变量。
var可以对一个变量声明多次,let只能对一个变量声明一次。
var name = "James";
var name = "David";
console.log(name);
var可以对一个变量声明多次,但这会导致一些问题。。
let camper = "James";
let camper = "David";
let只能对一个变量声明一次,上面的代码会报错:Uncaught SyntaxError: Identifier 'name' has already been declared
变量的命名规则:
- 变量有大小写字母、数字、下划线、$组成
- 不能使用保留关键词
- meaning & descriptive
- 不能以数字开头,不能包含空格或中划线
- 建议驼峰命名法,大小写敏感
声明多个变量:
let firstName = 'yin', lastName = 'bai'; // not recommend
let firstName = 'yin'; // recommend
let lastName = 'bai'; // recommend
constants
ES6还提出了一个定义常量的关键字const,const除了拥有let的所有特性之外,声明的变量不能被修改,即常量。
const声明的变量名应该全大写。
You should always name variables you don’t want to reassign using the const
keyword.
const interestRate = 0.3;
2.2 数据类型
Primitive types
JS中有两种数据类型,一种是primitive/value type,一种是reference type。primitive类型包括以下几种:
- String
- Number
- Boolean: true or false
- undefined:只声明变量未初始化时,也可以显示初始化。
- null:用于清空变量的值。
let name = 'yin';
let age = 27;
let isApproved = true;
let firstName = undefined;
let hobby = null;
dynamic typing
JS是一种动态编程语言。
静态编程语言:当声明变量时需要同时指定变量的类型,类型一旦指定就不能改变,如JAVA
动态编程语言:当声明变量时不需要指定变量的类型,变量类型可以在运行时改变,如python,javascript
可以使用typeof variable
查看变量当前的类型.
字符串
对字符串中的引号可以使用\转义;
对双引号可以使用单引号避免转义``
转义字符:
\' '
\" "
\\ backslash
\n new line
\r carriage return
\t tab
\b word boundary
\f form feed
使用+
连接字符串
使用+=
连接字符串
使用[]获取字符串的字符
JS中字符串string的不可变性
2.3 运算符
2.3.1 算术运算符
+
-
*
/
%
++, --
+=, -=, *=, /*
2.3.2 比较运算符
==
作为一种比较操作符equality operator
,用于比较两个变量的值是否相等,返回true/false。如果两个变量的类型不一致,会进行隐式类型转换成同一种类型后再进行比较。
1 == true // true
0 == true // false
1 == '1' // true
===
作为一种严格的比较操作符strict equality operator
,会比较两个变量的类型和值是否相等。与==
不同的是,不会进行类型转换。
1 === true // false
1 === '1' // false
!=
作为inequality operator
,与==
的返回值相反,在比较的时候也会进行类型转换。
!==
作为``the logical opposite of the strict equality operator,与
===`的返回值相反。
>, >=, <, <=
2.3.3 逻辑运算符
-
或||
-
针对布尔值,进行逻辑或运算;
-
针对其他值,将其转化为布尔值再进行计算。(1=true,其他值=false)
-
寻找第一个真值
result = value1 || value2 || value3;
-
-
与&&
- 针对布尔值,进行逻辑与运算
- 针对非布尔值,将其转化为布尔值再进行计算
- 寻找第一个假值
-
非!
- 将操作数转化成布尔类型,返回相反的值
-
优先级:非 > 与 > 或
2.4 objects
Reference types包括:object,array,function。定义一个object:
let person = {
name: 'yin',
age: 27
};
获取对象中属性的两种方法:
- dot notation:
person.name
- Bracket notation:
person['name']
dot notation简单明了,默认使用。
bracket notation在特定的场景下使用,比如属性名是用户的输入或者在程序运行过程中动态得到的,或者属性名中含有空格。
let key = 'name';
let value = 'john'
person[variable] = value;
更新对象中的属性:ourDog.bark = ‘woof’;
给对象中增加属性:ourDog.bark = ‘woof’;
删除对象中的属性:delete ourDog.bark;
遍历对象中的属性:
for (const key in object) {
if (Object.hasOwnProperty.call(object, key)) {
const element = object[key];
console.log(key, element)
}
}
获取对象中的所有key:Object.keys(object)
判断某个属性是否在对象中:object.hasOwnProperty(propname)
复杂对象:对象中包含对象
获取层级对象中的属性:obj1.obj2.prop
2.5 Arrays
Array is a data structure save a list of items.
let selectedColors = ['red', 'blue'];
console.log(selectedColors);
- 定义
- 多维数组
- 索引
- push(), pop(), shift(), unshift()
push()
always add element at the end of the array.pop()
always removes the last element of an array.shift()
always removes the first element instead of the last.unshift()
always add elements in front of the array.
2.6 Functions
A function is basically a set of statements that perform a task or calculates a value.
function greet(firstName, lastName) {
console.log('Hello ' + firstName + ' ' + lastName);
}
greet('John', 'White');
greet('Mary');
全局作用域:在函数块外部定义的变量具有全局作用域。
不使用let和const定义的变量同样具有全局作用域,应该始终使用let或const定义变量。
本地作用域:在函数内部定义的变量,和函数的参数一起,都只在函数内部生效。
函数内部可以有同名的全局变量和局部变量,局部变量优于全局变量。
2.7 流程控制
2.7.1 Conditional statements
JS中的条件语句包含:if…else和switch…case两种。
2.7.2 Loops
JS中实现循环有以下五种语法:
- for
let array = ['john', 'mary', 'lucy'];
for (let index = 0; index < array.length; index++) {
console.log(array[index]);
}
- for-each
let array = ['john', 'mary', 'lucy'];
array.forEach(element => {
console.log('hello ' + element);
});
- while
- do…while
- for…in
// for-in
let person = {
name: 'yin',
age: 27
};
for (const key in person) {
if (Object.hasOwnProperty.call(person, key)) {
const element = person[key];
console.log(key, element)
}
}
- for…of
- 循环 & 递归:将循环写法修改为递归写法
3. 其他
3.1 随机数
Math.random()
返回0-1之间左开右闭的随机数。
Math.floor()
向下取整。
生成0-m之间的整数:Math.floor(Math.random() * (m+1));
生成min到max之间的整数:Math.floor(Math.random() * (max - min + 1)) + min
3.2 解析字符串
parseInt(string, radix)
函数从字符串string按照radix进制解析一个整数。
3.3 三元表达式
condition ? expression1 : expression2
在expression中可以嵌套三元表达式。
condition1 ? expression1 : condition 2 ? expression2 : expression3
3.4 ES6新特性
var & let:使用var在函数外声明的变量是全局作用域,在函数内声明的变量时局部作用域。
const:不可变对象,避免重新声明,但是对象中的属性是可变的(list中的值,对象中的值)。
如果要避免对象中的值被改变,需要使用Object.freeze(obj)
来冻结对象。
4. JS OOP
OOP的四大特性:封装、抽象、继承、多态
对象:key-value对的集合
在JS中没有类这个概念。
创建对象的方式:
- 直接定义对象(不推荐)
const circle = {
radius: 1,
location: {
x: 1,
y: 1
},
draw: function() {
console.log("draw a circle");
}
}
circle.draw();
- 使用工厂方法创建并构造对象
// factory function
function createCircle(radius) {
return {
radius: radius,
draw: function() {
console.log("draw a circle");
}
}
}
const circle = createCircle(1);
circle.draw();
- 使用构造方法创建并构造对象
// constructor function
function Circle(radius) {
this.radius = radius;
this.draw = function() {
console.log("draw a circle");
}
}
let circle = new Circle(1);
circle.draw();
每一个object都有一个构造方法属性,这个方法表明了如何构造该对象。
In javascript, functions are objects
基本类型与引用类型
JS中的数据类型分为基本类型primitive type和引用类型reference type。基本类型复制的是值value,引用类型复制的是引用。
let x = 10;
let y = x;
x = 20; // y = 10
let a = {value: 10};
let b = a;
a.value = 20;
// b: {value: 20}
let number = 10;
function increase(number) {
number++;
}
increase(number);
console.log(number);// 10
let obj = { value: 10};
function increase(obj) {
obj.value++;
}
increase(obj);
console.log(obj); // {value: 11}
抽象
隐藏实现细节,只保留必要的接口。
在对象中,使用局部变量来实现对私有属性的隐藏。
function Circle(radius) {
this.radius = radius; // object property
let defaultLocation = { x: 0, y: 0 }; // local variable
let calculateOptimumLocation = function(factor) { // local variable
// ...
}
this.draw = function() { // object method
calculateOptimumLocation(0.1);
console.log('draw');
}
}
Getter/setter
对于对象的public属性和方法来说,可以使用dot notation或bracket notation来获取或者设置value。对于private属性来说,如何实现其getter/setter方法呢?
- 方法一:手动实现getter/setter方法,通过方法调用获得属性值。
function Circle(radius) {
this.radius = radius;
let defaultLocation = { x: 0, y: 0 };
this.getDefaultLocation = function() {
return defaultLocation;
}
this.draw = function() {
calculateOptimumLocation(0.1);
console.log('draw');
}
}
let circle = new Circle(1);
circle.getDefaultLocation(); // { x: 0, y: 0 }
- 方法二:基于
Object.defineProperty()
方法,和public属性一样。
function Circle(radius) {
this.radius = radius;
let defaultLocation = { x: 0, y: 0 };
this.draw = function() {
calculateOptimumLocation(0.1);
console.log('draw');
}
Object.defineProperty(this, 'defaultLocation', {
get: function() {
return defaultLocation;
}
})
}
let circle = new Circle(1);
circle.defaultLocation; // { x: 0, y: 0 }
练习
实现一个StopWatch对象,具有以下几个功能:
Start():开始计时;
stop():结束本次计时
duration:返回截止当前为止的计时时长,以秒为单位。
reset():重置计时器
function StopWatch(){
let status = 'Ready';
let startTime;
let duration = 0;
this.start = function() {
if (status === 'Running') {
throw new Error('The stopwatch is already running');
}
status = 'Running';
startTime = Date.now();
}
this.stop = function() {
if (status !== 'Running') {
throw new Error('The stopwatch is not running');
}
status = 'Ready';
duration += ((Date.now() - startTime) / 1000);
startTime = null;
}
this.reset = function() {
duration = 0;
}
Object.defineProperty(this, 'duration', {
get: function() {
return duration;
}
})
}