数据类型
js基本的数据类型:
-
Number: Any number, including numbers with decimals:
4,8,1516,23.42. -
BigInt: Any number, greater than 253-1 or less than -(253-1), with n appended to the number: 1234567890123456n.
-
String: Any grouping of characters on your keyboard (letters, numbers, spaces, symbols, etc.) surrounded by single quotes:
' ... 'or double quotes" ... ", though we prefer single quotes. Some people like to think of string as a fancy word for text. -
Boolean: This data type only has two possible values— either
trueorfalse(without quotes). It’s helpful to think of booleans as on and off switches or as the answers to a “yes” or “no” question. -
null:This data type represents the intentional absence of value
-
undefined:This data type is denoted by the keyword undefined.It also represents the absence of a value though it has a different use than
null.undefinedmeans that a given value does not exist.
null 和 undefined的区别 当一个变量被声明了,但没有赋值时,它的默认值就是undefined undefined还可以作为函数没有返回值时的默认返回值 null表示“无”,表示该变量没有值 简而言之,
undefined是缺少初始化的状态,null是一种空值的状态。
- symbol:A newer feature to the language, symbols are unique identifiers, useful in more complex coding. No need to worry about these for now.
- symbol是从es6开始引入的一种新特性,用于创建独一无二的标识符
- Object: Collections of related data.
运算符
- Add:
+ - Subtract:
- - Multiply:
* - Divide:
/ - Remainder:
% - 自增自减运算符
字符串拼接
Operators aren’t just for numbers! When a + operator is used on two strings, it appends the right string to the left string:
console.log('hi' + 'ya'); // Prints 'hiya'
console.log('wo' + 'ah'); // Prints 'woah'
console.log('I love to ' + 'code.')
// Prints 'I love to code.'
属性
当你在js中声明新的数据时,浏览器将会存下数据类型的实例(instance) 所有的数据类型都有特定属性,可以通过实例访问 比如说,每个String都有个length实例,存储了String中字符的个数(即长度)
console.log('Hello'.length); // Prints 5
方法
We call, or use, these methods by appending an instance with:
- a period (the dot operator)
- the name of the method
- opening and closing parentheses
e.g.'example string'.methodName().
console.log('hello'.toUpperCase()); // Prints 'HELLO'
console.log('Hey'.startsWith('H')); // Prints true
console.log(' Remove whitespace '.trim()); //prints Remove whitespace
变量
- es6之后使用let创建变量,之前用var
- If we don’t assign a value to a variable declared using the
letkeyword, it automatically has a value ofundefined. - We can reassign the value of the variable.
- 如果我们用let声明变量时不赋予任何值,它默认会是undefined
let changeMe = true;
changeMe = false;
console.log(changeMe)
常量
- es6之后用const声明常量
- Constant variables must be assigned a value when declared. If you try to declare a
constvariable without a value, you’ll get aSyntaxError. - 如果创建const时不赋予任何值,它会报错
字符串插值
const myPet = 'armadillo';
console.log(`I own a pet ${myPet}.`);
// Output: I own a pet armadillo.
模板字面量由反引号 ` 包裹
typeof 操作符
If you need to check the data type of a variable’s value, you can use the typeof operator.
The typeof operator checks the value to its right and returns, or passes back, a string of the data type.
const unknown1 = 'foo';
console.log(typeof unknown1); // Output: string
const unknown2 = 10;
console.log(typeof unknown2); // Output: number
const unknown3 = true;
console.log(typeof unknown3); // Output: boolean
条件语句
if
if (true) {
console.log('This message will print!');
}
// Prints: This message will print!
if……else
if (false) {
console.log('The code in this block will not run.');
} else {
console.log('But the code in this block will!');
}
// Prints: But the code in this block will!
条件运算符
- Less than:
< - Greater than:
> - Less than or equal to:
<= - Greater than or equal to:
>= - Is equal to:
=== - Is not equal to:
!==
10 < 12 // Evaluates to true
逻辑运算符
- the and operator (
&&) - the or operator (
||) - the not operator, otherwise known as the bang operator (
!)
if (stopLight === 'green' && pedestrians === 0) {
console.log('Go!');
} else {
console.log('Stop');
}
非Boolean值的真假
当你想查看一个变量是否存在,不必非要用它去和null或undefined作比较:
let myVariable = 'I Exist!';
if (myVariable) {
console.log(myVariable)
} else {
console.log('The variable does not exist.')
}
以上代码输出将会是myVariable,因为myVariable是一个真值,尽管它不为'true',但只要不为falsy数就是真值
falsy数即为:
0- Empty strings like
""or'' nullwhich represent when there is no value at allundefinedwhich represent when a declared variable lacks a valueNaN, or Not a Number
let numberOfApples = 0;
if (numberOfApples){
console.log('Let us eat apples!');
} else {
console.log('No apples left!');
}
// Prints 'No apples left!'
常见应用
验证用户名是否为空:
let username = '';
let defaultName;
if (username) {
defaultName = username;
} else {
defaultName = 'Stranger';
}
console.log(defaultName); // Prints: Stranger
当你的赋值里有或表达式时,js会选取真值进行赋值
let username = '';
let defaultName = username || 'Stranger';
console.log(defaultName); // Prints: Stranger
当都为true时,会选择第一个
三目运算符
一些简单的if……else语句:
let isNightTime = true;
if (isNightTime) {
console.log('Turn on the lights!');
} else {
console.log('Turn off the lights!');
}
可以用三目运算符代替:
isNightTime ? console.log('Turn on the lights!') : console.log('Turn off the lights!');
- The condition,
isNightTime, is provided before the?. - Two expressions follow the
?and are separated by a colon:. - If the condition evaluates to
true, the first expression executes. - If the condition evaluates to
false, the second expression executes.
函数
创建函数
- We should also be aware of the hoisting feature in JavaScript which allows access to function declarations before they’re defined.
- js具有hoisting(提升性),可以在函数声明之前访问函数
js函数构成:
- function 关键字
- 函数名
- 代码块
greetWorld(); // Output: Hello, World!
function greetWorld() {
console.log('Hello, World!');
}
调用函数
形参和实参
默认参数
es6的新特性是默认参数,默认参数允许参数有默认值,当调用函数时没有给参数或参数为undefined时会选择默认参数值
function greeting (name = 'stranger') {
console.log(`Hello, ${name}!`)
}
greeting('Nick') // Output: Hello, Nick!
greeting() // Output: Hello, stranger!
返回值
function rectangleArea(width, height) {
let area = width * height;
}
console.log(rectangleArea(5, 7)) // Prints undefined
如果一个函数没有返回值,那它的返回值默认为undefined
function rectangleArea(width, height) {
if (width < 0 || height < 0) {
return 'You need positive integers to calculate area!';
}
return width * height;
}
函数表达式
另一种定义函数的方法是通过函数表达式
在函数表达式中,通常不需要函数名。没有函数名的函数称为匿名函数。
A function expression is often stored in a variable in order to refer to it. 函数表达式将函数存储在变量中,可以通过变量引用它
声明函数表达式:
- 声明一个变量,从es6开始,通常使用const声明
- 赋予变量一个匿名函数
函数表达式没有hoisting特性
const plantNeedsWater = function(day){
if(day === 'Wednesday'){
return true;
}else{
return false;
}
};
plantNeedsWater('Tuesday');
console.log(plantNeedsWater('Tuesday'));
为什么需要函数表达式
- 函数表达式非常适合作为回调函数
setTimeout(function() {
console.log("1秒后执行");
}, 1000);
如果用函数声明,就必须先在外部定义函数:
function delayedMessage() {
console.log("1秒后执行");
}
setTimeout(delayedMessage, 1000);
回调函数:函数表达式更适合在需要“立即传递函数”的场景,如 setTimeout、map()、filter()。
- 作为匿名函数
const greet = function(name) {
return "Hello, " + name;
};
console.log(greet("Alice")); // 输出: Hello, Alice
优点:
- 简洁,适用于临时函数
- 可以作为参数传递,不污染全局作用域
- 立即执行函数
函数表达式可以用来 创建一个立即执行的函数(IIFE,Immediately Invoked Function Expression) :
javascript
复制编辑
(function() {
console.log("立即执行!");
})();
- 作为对象的方法,函数表达式可以直接用于对象的方法:
const person = {
name: "Alice",
sayHi: function() {
console.log("Hi, I'm " + this.name);
}
};
person.sayHi(); // 输出: Hi, I'm Alice
如果用函数声明,不能直接写在对象里
- 用于高阶函数 在 函数式编程 中,函数表达式可以赋值给变量,并传递给其他函数:
const add = function(a, b) {
return a + b;
};
function operate(fn, x, y) {
return fn(x, y);
}
console.log(operate(add, 3, 4)); // 输出: 7
这里 add 作为参数传递给 operate,如果是函数声明,就不能这样传递。
箭头函数
es6开始引入箭头函数,一种简单的书写函数的方法
- 箭头函数去除了关键词function,而是用()代替
- =>指向函数体
const rectangleArea = (width, height) => {
let area = width * height;
return area;
};
-
只有一个入参的函数,不需要用括号
-
只有一行代码的函数体,不需要用括号,在没有大括号的情况下,该行执行结果会被自动返回,不需要显式写return
如:
const squareNum = (num) => {
return num * num;
};
等于
const squareNum = num => num * num;
作用域
作用域定义了变量可以在哪里被使用
代码块和作用域
代码块作用于函数定义和if语句:
const logSkyColor = () => {
let color = 'blue';
console.log(color); // blue
}
if (dusk) {
let color = 'pink';
console.log(color); // pink
}
全局变量
- 全局变量通常声明在代码块之外
- 全局变量可以被程序内的任何代码使用,包括代码块内
const color = 'blue';
const returnSkyColor = () => {
return color; // blue
};
console.log(returnSkyColor()); // blue
局部变量
- 局部变量被定义在代码块内,只能在代码块内被使用
const logSkyColor = () => {
let color = 'blue';
console.log(color); // Prints "blue"
};
logSkyColor(); // Prints "blue"
console.log(color); // throws a ReferenceError
作用域污染
- 过多的全局变量可能会造成问题
- 当你声明全局变量时,这些变量会进入global namespace
- global namespace允许从程序中的任何地方访问全局变量,这些变量会在global namespace中一直存在到程序运行结束
- 如果我们使用过多的全局变量,global namespace很快就会满
- 作用域污染是指我们创建太多全局变量,或者我们在不同作用域之间重复使用变量,即在不同的代码块中声明同名变量
let num = 50;
const logNum = () => {
num = 100; // Take note of this line of code
console.log(num);
};
logNum(); // Prints 100
console.log(num); // Prints 100
数组
js的数组可以存储任何数据类型(包括字符串,数字,布尔型)。数组是有序的,意味着每个元素都有一个索引
创建数组
js可以创建全是同一数据类型的数组,也可以创建全是不同数据类型的数组
let newYearsResolutions = ['Keep a journal', 'Take a falconry class', 'Learn to juggle'];
const hobbies = ['a', 'b', 'c'];
console.log(hobbies);
//[ 'a', 'b', 'c' ]
访问数组元素
-
数组中的每一个元素都一个数字表示其位置,即索引
-
我们可以通过索引访问其中的单个元素,js的索引从0开始计算
-
如果访问数组长度以外的元素,会返回undefined
const hello = 'Hello World';
console.log(hello[6]);
// Output: W
更新数组元素
let seasons = ['Winter', 'Spring', 'Summer', 'Fall'];
seasons[3] = 'Autumn';
console.log(seasons);
//Output: ['Winter', 'Spring', 'Summer', 'Autumn']
let/const数组
- 用const声明的变量是不可以再重新赋值的,但是,用const声明的数组中的元素是可以变化的,我们可以改变const数组的内容,但是不能重新赋值一个新数组或一个不同的值
length属性
- length会返回数组中元素的个数
const newYearsResolutions = ['Keep a journal', 'Take a falconry class'];
console.log(newYearsResolutions.length);
// Output: 2
push方法
push方法允许我们添加一个元素在数组末尾
const itemTracker = ['item 0', 'item 1', 'item 2'];
itemTracker.push('item 3', 'item 4');
console.log(itemTracker);
// Output: ['item 0', 'item 1', 'item 2', 'item 3', 'item 4'];
pop方法
pop方法会删除数组中的最后一个元素,且pop方法本身会返回被删除的那个元素
const newItemTracker = ['item 0', 'item 1', 'item 2'];
const removed = newItemTracker.pop();
console.log(newItemTracker);
// Output: [ 'item 0', 'item 1' ]
console.log(removed);
// Output: item 2
其他Array方法
当调用pop和push时,会改变原数组,如果我们不想改变原数组,也存在不会改变元素组的方法:
shift:移除数组中的第一个元素
unshift(item):将元素插到数组中第一个位置
slice(start index, end index):截取数组中的一部分
indexOf(item):返回item所在的索引
数组和函数
之前我们学习了可变方法和不可变方法,那么如果作为函数参数对数组进行修改,数组会变化吗
const flowers = ['peony', 'daffodil', 'marigold'];
function addFlower(arr) {
arr.push('lily');
}
addFlower(flowers);
console.log(flowers); // Output: ['peony', 'daffodil', 'marigold', 'lily']
会变的,因为是引用传递
嵌套数组
数组中也可以存放其他数组,当数组中有元素为数组时,这就是一个嵌套数组
const nestedArr = [[1], [2, 3]];
console.log(nestedArr[1]); // Output: [2, 3]
const nestedArr = [[1], [2, 3]];
console.log(nestedArr[1]); // Output: [2, 3]
console.log(nestedArr[1][0]); // Output: 2
循环
for循环
for (let counter = 0; counter < 4; counter++) {
console.log(counter);
}
遍历数组
const animals = ['Grizzly Bear', 'Sloth', 'Sea Lion'];
for (let i = 0; i < animals.length; i++){
console.log(animals[i]);
}
嵌套循环
const myArray = [6, 19, 20];
const yourArray = [19, 81, 2];
for (let i = 0; i < myArray.length; i++) {
for (let j = 0; j < yourArray.length; j++) {
if (myArray[i] === yourArray[j]) {
console.log('Both arrays have the number: ' + yourArray[j]);
}
}
}
while循环
for循环转while循环
// A for loop that prints 1, 2, and 3
for (let counterOne = 1; counterOne < 4; counterOne++){
console.log(counterOne);
}
// A while loop that prints 1, 2, and 3
let counterTwo = 1;
while (counterTwo < 4) {
console.log(counterTwo);
counterTwo++;
}
do……while循环
let countString = '';
let i = 0;
do {
countString = countString + i;
i++;
} while (i < 5);
console.log(countString);
break
for (let i = 0; i < 99; i++) {
if (i > 2 ) {
break;
}
console.log('Banana.');
}
console.log('Orange you glad I broke out the loop!');
函数
函数为数据
const announceThatIAmDoingImportantWork = () => {
console.log("I’m doing very important work!");
};
const busy = announceThatIAmDoingImportantWork;
busy(); // This function call barely takes any space!
- 因为原函数名太长所以换成了busy
- 原函数名可以通过busy.name得到
函数为参数
-
在js中,函数可以作为参数传递
-
作为参数传递进来的函数叫回调函数,回调函数会在高阶函数执行中被调用
const higherOrderFunc = param => {
param();
return `I just invoked ${param.name} as a callback function!`
}
const anotherFunc = () => {
return 'I\'m being invoked by the higher-order function!';
}
higherOrderFunc(anotherFunc);
const addTwo = num => {
return num + 2;
}
const checkConsistentOutput = (func, val) => {
let checkA = val + 2;
let checkB = func(val);
if(checkA === checkB){
return checkA;
}else{
return 'inconsistent results';
}
}
console.log(checkConsistentOutput(addTwo, 2));
迭代器
迭代器是调用在数组上的方法,用于迭代数组并返回值:
.forEach().map().filter()
const artists = ['Picasso', 'Kahlo', 'Matisse', 'Utamaro'];
artists.forEach(artist => {
console.log(artist + ' is one of my favorite artists.');
});
const numbers = [1, 2, 3, 4, 5];
const squareNumbers = numbers.map(number => {
return number * number;
});
console.log(squareNumbers);
const things = ['desk', 'chair', 5, 'backpack', 3.14, 100];
const onlyNumbers = things.filter(thing => {
return typeof thing === 'number';
});
console.log(onlyNumbers);
foreach
foreach用于对每个元素执行相同的代码
- foreach可以将回调函数作为参数
.forEach()遍历数组,并对每个元素执行回调函数。在每次执行时,当前元素作为参数传递给回调函数。- foreach的返回值永远是undefined
通常我们会结合foreach和箭头函数
groceries.forEach(groceryItem => console.log(groceryItem));
我们也可以提前定义一个函数作为回调函数
function printGrocery(element){
console.log(element);
}
groceries.forEach(printGrocery);
示例:
const fruits = ['mango', 'papaya', 'pineapple', 'apple'];
// Iterate over fruits below
fruits.forEach(fruit => console.log("I want to eat a " + fruit))
map
map也可以将回调函数作为参数,与forEach的不同是它会返回修改过的新数组
const numbers = [1, 2, 3, 4, 5];
const bigNumbers = numbers.map(number => {
return number * 10;
});
console.log(numbers); // Output: [1, 2, 3, 4, 5]
console.log(bigNumbers); // Output: [10, 20, 30, 40, 50]
- map会将每个元素作为参数调用回调函数,并将结果返回在新的数组
示例:
const animals = ['Hen', 'elephant', 'llama', 'leopard', 'ostrich', 'Whale', 'octopus', 'rabbit', 'lion', 'dog'];
// Create the secretMessage array below
const secretMessage = animals.map(animal => {return animal[0];});
console.log(secretMessage.join(''));
const bigNumbers = [100, 200, 300, 400, 500];
// Create the smallNumbers array below
const smallNumbers = bigNumbers.map(bigNumber => {return bigNumber/100;});
filter
- filter也可以像map一样返回新数组,但filter返回的新数组是经过筛选的
- filter会根据回调函数调用元素后返回的真/假值,判断是否过滤掉这个元素
- 返回结果为真的元素会被加进新数组
const words = ['chair', 'music', 'pillow', 'brick', 'pen', 'door'];
const shortWords = words.filter(word => {
return word.length < 6;
});
console.log(words); // Output: ['chair', 'music', 'pillow', 'brick', 'pen', 'door'];
console.log(shortWords); // Output: ['chair', 'music', 'brick', 'pen', 'door']
findIndex()
有时我们想知道某个元素在数组中的位置,这时就会用到findIndex方法
const jumbledNums = [123, 25, 78, 5, 9];
const lessThanTen = jumbledNums.findIndex(num => {
return num < 10;
});
console.log(lessThanTen); // Output: 3
console.log(jumbledNums[3]); // Output: 5
- findIndex会返回数组中第一个符合条件的元素的索引
- 如果没有任何元素符合条件,findIndex会返回-1
reduce()
- reduce 可以用来处理数组的各种累积操作,通过将数组元素逐一处理并累加或合并,最终得到一个单一结果
const numbers = [1, 2, 4, 10];
const summedNums = numbers.reduce((accumulator, currentValue) => {
return accumulator + currentValue
})
console.log(summedNums) // Output: 17
-
reduce会按照固定的顺序将数组元素逐一传递给回调函数,并且回调函数的第一个参数总是作为累计值(上一次回调的结果),第二个参数总是当前正在处理的数组元素
-
reduce也可以通过第二个参数去设置初始值(accumulator的初始值),例如:
const numbers = [1, 2, 4, 10];
const summedNums = numbers.reduce((accumulator, currentValue) => {
return accumulator + currentValue
}, 100) // <- Second argument for .reduce()
console.log(summedNums); // Output: 117
some
- 检查数组中至少有一个元素满足指定条件
const words = ['unique', 'uncanny', 'pique', 'oxymoron', 'guise'];
// Something is missing in the method call below
console.log(words.some((word) => {
return word.length < 6;
}));
every
用来检查数组中的 每一个元素 是否都符合指定的条件
console.log(interestingWords.every((word) => {return word.length > 5} ));
对象
创建对象
- 我们用{}来创建对象
let spaceship = {}; // spaceship is an empty object
- js的对象是无序的
- 数据以键值对的形式存在
- 可以用数字符号字符串作为对象的键,但它们最终会被转换成字符串(自动)
- 不带引号的键可以直接作为对象的属性名,但必须符合命名规范,带引号的键可以任意命名
// An object literal with two key-value pairs
let spaceship = {
'Fuel Type': 'diesel',
color: 'silver'
};
访问对象属性
访问对象属性有两种方式:
1.通过dot
let spaceship = {
homePlanet: 'Earth',
color: 'silver'
};
spaceship.homePlanet; // Returns 'Earth',
spaceship.color; // Returns 'silver',
- 如果我们访问不存在的属性,就会得到undefined
2.方括号表示法
let spaceship = {
'Fuel Type': 'Turbo Fuel',
'Active Duty': true,
homePlanet: 'Earth',
numCrew: 5
};
spaceship['Active Duty']; // Returns true
spaceship['Fuel Type']; // Returns 'Turbo Fuel'
spaceship['numCrew']; // Returns 5
spaceship['!!!!!!!!!!!!!!!']; // Returns undefined
属性的赋值
- 如果属性已经存在在对象中,则会代替旧值
- 如果属性不存在方法中,则会创建一个新的属性
- 尽管我们无法直接改变const变量所指向的对象,但是我们可以改变这个对象的属性或创建新的属性
const spaceship = {type: 'shuttle'};
spaceship = {type: 'alien'}; // TypeError: Assignment to constant variable.
spaceship.type = 'alien'; // Changes the value of the type property
spaceship.speed = 'Mach 5'; // Creates a new key of 'speed' with a value of 'Mach 5'
也可以通过delete关键字删除属性:
- 删除对象属性时,点表示法只支持符合命名规范的属性
- 包括空格,特殊字符,动态生成的属性名,只能用方括号表示法
const spaceship = {
'Fuel Type': 'Turbo Fuel',
homePlanet: 'Earth',
mission: 'Explore the universe'
};
delete spaceship.mission; // Removes the mission property
delete spaceship['Secret Mission'];
对象中的方法
当函数作为数据存储在对象中时,我们可以称其为方法
变量是对象有什么,方法是对象可以做什么
在对象中创建方法,key就是函数名,value就是匿名函数表达式:
const alienShip = {
invade: function () {
console.log('Hello! We have come to dominate your planet. Instead of Earth, it shall be called New Xaculon.')
}
};
在es6之后我们可以省略冒号和function关键字:
const alienShip = {
invade () {
console.log('Hello! We have come to dominate your planet. Instead of Earth, it shall be called New Xaculon.')
}
};
对象函数的调用是通过对象名和方法名:
alienShip.invade(); // Prints 'Hello! We have come to dominate your planet. Instead of Earth, it shall be called New Xaculon.'
同一个对象中的方法与方法要用逗号隔开:
let alienShip = {
retreat(){
console.log(retreatMessage);
},
takeOff(){
console.log('Spim... Borp... Glix... Blastoff!');
}
};
嵌套对象
在实际应用中,对象经常是嵌套的,即对象可以将其他对象作为变量
const spaceship = {
telescope: {
yearBuilt: 2018,
model: '91031-XLT',
focalLength: 2032
},
crew: {
captain: {
name: 'Sandra',
degree: 'Computer Engineering',
encourageTeam() { console.log('We got this!') }
}
},
engine: {
model: 'Nimbus2000'
},
nanoelectronics: {
computer: {
terabytes: 100,
monitors: 'HD'
},
'back-up': {
battery: 'Lithium',
terabytes: 50
}
}
};
链式调用:
spaceship.nanoelectronics['back-up'].battery; // Returns 'Lithium'
按引用传递
js中对象是根据引用传递的
homePlanet : 'Earth',
color : 'silver'
};
let paintIt = obj => {
obj.color = 'glorious gold'
};
paintIt(spaceship);
spaceship.color // Returns 'glorious gold'
const spaceship = {
homePlanet : 'Earth',
color : 'silver'
};
let paintIt = obj => {
obj.color = 'glorious gold'
};
paintIt(spaceship);
spaceship.color // Returns 'glorious gold'
let spaceship = {
homePlanet : 'Earth',
color : 'red'
};
let tryReassignment = obj => {
obj = {
identified : false,
'transport type' : 'flying'
}
console.log(obj) // Prints {'identified': false, 'transport type': 'flying'}
};
tryReassignment(spaceship) // The attempt at reassignment does not work.
spaceship // Still returns {homePlanet : 'Earth', color : 'red'};
spaceship = {
identified : false,
'transport type': 'flying'
}; // Regular reassignment still works.
- 当我们尝试重新赋值传入对象时,赋值并没有生效
- 当我们将
spaceship传递给那个函数时,obj变成了指向spaceship对象内存位置的引用,但并没有指向spaceship变量。 - 这是因为
tryReassignment()函数的obj参数是它自己的一个变量,tryReassignment()函数的代码体完全不知道spaceship变量的存在 - 所以obj指向了一个新的对象,但这只会影响obj变量,不会印象外部的spaceship变量
通过对象循环
我们之前学习了如何通过数组的数字索引遍历数组,但对象中的键值对是无序的,js提供了一种通过for…in语法遍历对象的解决方案
for…in会执行给出的代码块中每一个变量
let spaceship = {
crew: {
captain: {
name: 'Lily',
degree: 'Computer Engineering',
cheerTeam() { console.log('You got this!') }
},
'chief officer': {
name: 'Dan',
degree: 'Aerospace Engineering',
agree() { console.log('I agree, captain!') }
},
medic: {
name: 'Clementine',
degree: 'Physics',
announce() { console.log(`Jets on!`) } },
translator: {
name: 'Shauna',
degree: 'Conservation Science',
powerFuel() { console.log('The tank is full!') }
}
}
};
// for...in
for (let crewMember in spaceship.crew) {
console.log(`${crewMember}: ${spaceship.crew[crewMember].name}`);
}
- for…in循环会遍历spaceship.crew中的每一个元素,在每次迭代中,crewMember会设置为spaceship.crew中的一个键,从而使我们能够打印出每个船员的名字
对象的进一步学习
this
const goat = {
dietType: 'herbivore',
makeSound() {
console.log('baaa');
}
};
看起来可以成功运行,但如果我们再加一个diet方法让它输出dietType的值?
const goat = {
dietType: 'herbivore',
makeSound() {
console.log('baaa');
},
diet() {
console.log(dietType);
}
};
goat.diet();
// Output will be "ReferenceError: dietType is not defined"
- 为什么dietType会成为未定义的变量,这是因为在diet的作用域内,我们不能自动访问goat的其他属性,这时需要用到this关键字
const goat = {
dietType: 'herbivore',
makeSound() {
console.log('baaa');
},
diet() {
console.log(this.dietType);
}
};
goat.diet();
// Output: herbivore
this关键字引用的是调用对象,它提供了访问调用对象属性的能力
箭头函数和this
当我们在方法中使用箭头函数和this时,情况会变得复杂一点,例如:
const goat = {
dietType: 'herbivore',
makeSound() {
console.log('baaa');
},
diet: () => {
console.log(this.dietType);
}
};
goat.diet(); // 输出 undefined
从注释中可以看出,调用goat.diet()后,打印的结果是undefined
当diet方法是由箭头函数定义时,箭头函数会自动绑定this,并且this的值不会指向调用该方法的对象,而是指向定义时的作用域,在上面的代码段中,this的值是全局对象(浏览器下为windows对象,Node.js中位global对象,而全局对象中并没有dietType视讯跟,因此返回的是undefined)
所以在方法中使用this时,应该尽量避免使用箭头函数
私有
访问和修改变量是项目中最基本的工作,然而有时我们不想让其他代码随意修改和访问变量
在讨论对象的隐私性时,我们将其定义为只有特定的属性应当是可变的或能够改变其值的。
在js中,通常约定私有变量前有下划线:
const bankAccount = {
_amount: 1000
}
bankAccount._amount = 1000000;
Getters
Getters是第一种拿到和返回对象中内部变量的方法,但是它的作用不止取数
const person = {
_firstName: 'John',
_lastName: 'Doe',
get fullName() {
if (this._firstName && this._lastName){
return `${this._firstName} ${this._lastName}`;
} else {
return 'Missing a first name or a last name.';
}
}
}
// To call the getter method:
person.fullName; // 'John Doe'
- 我们通过get关键字声明了一个函数
- 在函数中加了条件判断
- getter函数的调用不用加括号,所以看起来会像属性
getter的好处:
- getter可以在拿变量时同时对数据进行操作
- getter可以根据条件返回不同的数据
- getter中可以使用this
- 使代码更加易读
使用getter/setter方法时需要注意的是,属性不能与getter/setter方法同名,如果这样做,调用时会导致无限调用栈错误
setter
我们同样可以创建setter方法来赋值对象中已存在的变量
const person = {
_age: 37,
set age(newAge){
if (typeof newAge === 'number'){
this._age = newAge;
} else {
console.log('You must assign a number to age');
}
}
};
- 可以注意到上例中,setter可以起到数据检验的作用
- 当使用setter方法时,只有this._age变量会改变
- 可以根据变量的值有不同的输出
person.age = 40;
console.log(person._age); // Logs: 40
person.age = '40'; // Logs: You must assign a number to age
- setter方法不需要加括号调用,像setter方法一样,可以检查输入,执行变量操作,使代码更有易读性
- 尽管有了setter函数,还是有可能直接修改对象中的变量,例如上面的例子中,我们直接修改变量:
person._age = 'forty-five'
console.log(person._age); // Prints forty-five
例子:
const robot = {
_model: '1E78V2',
_energyLevel: 100,
_numOfSensors: 15,
get numOfSensors(){
if(typeof this._numOfSensors === 'number'){
return this._numOfSensors;
} else {
return 'Sensors are currently down.'
}
},
set numOfSensors(num){
if(typeof num === 'number' && num >= 0){
this._numOfSensors = num;
}else{
console.log('Pass in a number that is greater than or equal to 0');
}
}
};
robot.numOfSensors = 100;
console.log(robot.numOfSensors);
工厂函数
我们已经掌握了创建单个对象的方法,但是很多时候我们想快速创建多个实例,这就需要引进工厂函数
工厂函数是可以返回对象并可以复用创建多种对象实例,也可以定制返回的对象
const monsterFactory = (name, age, energySource, catchPhrase) => {
return {
name: name,
age: age,
energySource: energySource,
scare() {
console.log(catchPhrase);
}
}
};
- monsterFactory 有四个参数,并且会返回一个对象
const ghost = monsterFactory('Ghouly', 251, 'ectoplasm', 'BOO!');
ghost.scare(); // 'BOO!'
现在我们通过调用monsterFactory方法获取了一个ghost对象
属性值简写
es6新增特性
const monsterFactory = (name, age) => {
return {
name: name,
age: age
}
};
可以简写成:
const monsterFactory = (name, age) => {
return {
name,
age
}
};
解构赋值
我们经常想从对象中提取键值对并将它们存储在对象中
const vampire = {
name: 'Dracula',
residence: 'Transylvania',
preferences: {
day: 'stay inside',
night: 'satisfy appetite'
}
};
如果我们想要提取出residence并存储到变量中,通常会这么做:
const residence = vampire.residence;
console.log(residence); // Prints 'Transylvania'
然而,我们也可以通过解构赋值提取对象中的变量,例如:
const { residence } = vampire;
console.log(residence); // Prints 'Transylvania'
- vampire :对象名
- residence : 要提取的变量名
我们也可以用解构赋值获取嵌套中的对象变量:
const { day } = vampire.preferences;
console.log(day); // Prints 'stay inside'
内置对象方法
Object.assign()、Object.entries() 和 Object.keys()等
Object也有一些内置对象方法,例如Object.assign()、Object.entries() 和 Object.keys()等
const robot = {
model: 'SAL-1000',
mobile: true,
sentient: false,
armor: 'Steel-plated',
energyLevel: 75
};
// What is missing in the following method call?
const robotKeys = Object.keys(robot);
- Object.Keys():获取对象的所有键,并将它们存储到数组中。
- Object.entries():返回一个数组,数组中包含多个子数组,每个子数组都包含对象中属性的键和值
- Object.assign():将一个或多个源对象的属性复制到目标对象中
const newRobot = {};
Object.assign(newRobot,robot,{laserBlaster: true, voiceRecognition: true});
console.log(newRobot);
在这段代码中,assign会将属性复制到target也就是第一个变量上,之后的变量都是源对象,它们的属性会被复制到 target