JS基础:基本语法

2019-08-28  本文已影响0人  意一ineyee

目录

一. 常量与变量
 1. 常量
 2. 变量
二. 数据类型
 1. 概述
 2. 基本数据类型
 3. 对象数据类型
 4. 特殊数据类型
 5. 数据类型的强制转换
三. 运算符
 1. 加法运算符
 2. 严格相等运算符(===)和相等运算符(==

一. 常量与变量


常量是指初始化后,就不能再改变的变量。

变量是指初始化后,还可以再改变的变量。

  • 变量本身是一个指针,给变量初始化就是把这个指针指向某个对象。

  • 而这里所说的“变量在初始化后,还可以再改变”,是指能改变变量本身这个指针的值。(现象就是,能给这个变量重新赋值,如string = ...

  • 至于变量所指向那个对象的值能否改变就要看这个变量是不是可变变量了。(现象就是,能不能通过这个变量调用对象的方法来修改对象的值,如[string appendString:...][string appendString:...]其实是根据string这个指针找到它对应的对象,让那个对象调用appendString:方法,而不是string直接调用appendString:方法。NSString创建的string就是不可变变量,NSMutableString创建的string就是可变变量)

可变属性和不可变属性

  • 属性其实就是变量,只不过这些变量和类建立了某种关联,可以通过点语法来访问而已,为了区别于普通的全局变量和局部变量,我们把这些变量称为了属性。

  • 既然敲定了属性就是变量,那变量可分为可变变量和不可变变量,自然属性也就可分为可变属性和不可变属性,只不过在变量那里我们一般很少提到可变不可变这个概念,而在属性这里常提到而已,但不管怎么说,无论是可变属性还是不可变属性,它们在初始化之后,就都还是可以再改变的,即可以改变属性的值。

  • 和可变变量一样,可变属性是指那些既可以修改属性本身这个指针的值、也可以修改指针所指向那个对象的值的属性。可变属性是指既可以修改属性原来的值,也可以给属性重新赋值。(前面一句说全了就是“也可以拿这个属性调用对象的方法来修改对象原来的值”)

  • 和不可变变量一样,不可变属性是指那些只能修改属性本身这个指针的值的属性,不能修改指针所指向那个对象的值。不可变属性是指不能修改属性原来的值,但能给属性重新赋值。(前面一句说全了就是“不能拿这个属性调用对象的方法来修改对象原来的值”)

因此,我们不能把不可变属性和常量这两个给混淆在一起,它们是不一样的,不可变属性是可以重新赋值的,而常量是无法重新赋值的。那可能你又要问,“不可变属性既然可以重新赋值,那它为什么叫不可变属性呢”?回顾上面,我们可以得出答案,不可变属性的“不可变”三个字,指的不是它本身不可变,而是它所指向的对象不可变。

1. 常量

JS用const关键字来定义一个常量,常量一旦初始化后,它的值就不能再更改,否则会报错。

const PI = 3.1415;
PI;// 3.1415

PI = 3.1415926;// TypeError: Assignment to constant variable.

注意,所有的匿名函数赋值给变量时,都应该使用常量,这样就保证了func变量的值不会被修改,即func变量不会被重新指向另一个函数的实现。

const func = function () {
    console.log(11);
}

2. 变量

JS用varlet关键字来定义变量,而且只能用它俩来定义变量,这就意味着用varlet定义的变量可以指向任意数据类型的数据,而且变量的类型也是可以随时更改的,这是没有问题的,因为JS也是一门动态类型的语言。

var a = 1;// 数值
var b = 'hello';// 字符串

var a = 1;// 原本是个数值
a = 'hello';// 突然变成了字符串

注意:

let是ES6新增的关键字,它可以避免掉一些var会导致的问题,具体是什么可以自己去查,我们平常就直接使用let好了。

当我们在全局环境中定义变量时,要考虑一下,这个变量到底会不会被修改,如果不会被修改的话,那我们最好是用const把它定以常量,一来别人一看到这个地方立马就会意识到不能修改这个变量,二来避免别人误修改某个变量导致的程序错误。

二. 数据类型


1. 概述

JS里的数据类型其实不多,一共就三类八种:

JS里提供了两种常用的方式来判断一个数据是哪种类型,typeof运算符和instanceof运算符:

typeof false;// "boolean"
typeof 123;// "number"
typeof 'hi';// "string"

typeof {};// "object"
typeof [];// "object"
typeof function () {};// "function"

typeof undefined;// "undefined"
typeof null;// "object"


// 自定义类
class Person {

}
// 自定义类的对象
let person = new Person();
typeof person;// "object"
typeof Person;// "function"

我们可以看到数组的类型是Object,而不是Array,也就是说在JS里数组本质上是一种特殊的对象。

我们还可以看到null的类型也是Object,这是历史遗留问题,较低版本的JS只会把null作为Object对象的一个特殊值来看待,后来才把它拖出来作为一个特殊数据类型的,但是为了兼容低版本,就保持了null的类型为Object

我们还可以看到自定义类的类型为Function,而不是Person类的元类,这是因为JS里的类其实不像我们OC里的类,它仅仅是对构造函数的封装而已,还有person对象的类型也不是Person类,而是object

([]) instanceof Array;// true
({}) instanceof Object;// true
(function() {}) instanceof Function;// true

// 自定义类
class Person {

}
// 自定义类的对象
let person = new Person();
person instanceof Person;// true

那么综上,请注意:

我们只需要了解一下上面这些数据类型的真实关系就可以了,实际上为了方便理解,我们还是会对照OC把JS里的数据类型分为三类若干种:

  • 基本数据类型:布尔值(Boolean)、数值(Number)、字符串(String
  • 对象数据类型:JS对象(Object)、数组(Array)、函数(Function)、JSON对象(JS里特有的一个类,算是一个数据解析工具类了,用来在JSON字符串和JS对象之间相互转换)、Date(类似于OC的NSDate,搞时间用的一个类,用到时自己去查即可)、Math(提供了各种数学功能,用到时自己去查即可)、RegExp(搞正则表达式的一个类,用到时自己去查即可)、自定义类(后面面向对象编程时会说到)
  • 特殊数据类型:undefinednull

2. 基本数据类型

2.1 布尔值

JS里的布尔值用truefalse两个关键字表示。

2.2 数值

JS里的整数和小数统称为数值。

2.3 字符串

JS里的字符串可以用单引号''或双引号"",但是由于HTML标签的属性值使用双引号,为了避免混淆,我们在开发中最好使用单引号''

虽说JS里的字符串是基本数据类型,但其实JS也为它封装了对象才有的一些属性和方法,我们完全可以把它当作OC里的可变字符串来看待。

(1)字符串的常用属性

length属性用来返回字符串的长度。

'hi ineyao'.length;// 5

(2)字符串的常用方法

substr()方法用来获取一个字符串的子字符串,它需要传入两个参数,第一个参数是子字符串开始的下标(包含开始下标),第二个参数是子字符串的长度。

'JavaScript'.substr(4, 6);// "Script"

如果我们只传入一个参数,substr()方法会把该参数看作子字符串开始的下标,而子字符串长度为直到字符串结束。

'JavaScript'. substr(4);// "Script"

slice()方法用来获取一个字符串的子字符串,它需要传入两个参数,第一个参数是子字符串开始的下标(包含开始下标),第二个参数是子字符串结束的下标(不包含结束下标)。

'JavaScript'.slice(0, 4);// "Java"

如果我们只传入一个参数,slice()方法会把该参数看作子字符串开始的下标,而子字符串结束的下标为字符串的结束。

'JavaScript'.slice(4);// "Script"

split()方法用来将字符串按指定的字符给切开,并返回由切开后字符组成的数组。

'a@b@c'.split('@');// ["a", "b", "c"]

3. 对象数据类型

3.1 JS对象类或字典类

Object直接翻译过来是“对象”,这个类就叫作“对象类”,那我们如何创建一个对象类的对象呢?

let obj = {
    'name': '张三',
    'sex': '男',
    'age': 11
};

上面的代码就创建了一个对象类的对象,用大括号{}就行,是不是和我们OC里创建字典一模一样?使用起来似乎风平浪静没什么问题,但其实在谈到Object类创建对象的时候,我总觉得有点别扭,现在分析一下别扭的原因:

在OC里,如果有人对你说“你创建一个对象”,你会下意识地想到他是让你创建一个自定义类的对象,而不是创建系统提供的类的对象,因为他如果想让你创建系统提供的类(如NSDictionary)的对象的话,就会直接跟你说“你创建一个字典”,绝对不会用“对象”来称呼“字典”。

那到了JS里,由于我保持了OC的思维惯性,当有人对我说“你创建一个对象”时,这一步我就开始迷茫了,我不能再像OC那样思考,而是得首先考虑一步你到底是要我创建系统提供的类——Object类——的对象呢,还是要我创建自定义类的对象呢?你看,就是思维上需要多添加这么一点点小环节,我就觉得很别扭。

因此,为了消除这点小别扭,我的解决办法是不把Object类叫作“对象类”,这样它的对象就不直接叫作“对象”了,那创建Object类的对象时自然也就不叫作“创建一个对象”了,这样就可以和自定义类区分开,我也就可以按照OC那样思考了,我把Object类叫作“JS对象类”,Object类的对象叫作“JS对象”,如果你更过分一点,甚至可以直接把Object类叫作“字典类”,Object类的对象叫作“字典”,因为JS对象和OC字典真得一模一样嘛,我们完全可以把JS对象当作字典来看待,而且是当作可变字典来看待,同时它还具有对象才有的一些语法糖功能。

(1)JS对象的常用操作(和我们OC里可变字典的操作基本一样)

JS对象的key必须是字符串,value可以是任意数据类型,我们既然都叫人家对象了,那JS对象的key又叫作属性,value又叫作属性的值。

let obj = {
    'name': '张三'
};

// 读
obj['name'];// 字典式读法
obj.name;// 对象式读法

// 写
obj['name'] = '李四';// 字典式写法
obj.name = '王五';// 对象式写法
let obj = {
    'name': '张三',
};

// 增:你不用管它原来有没有,直接给它点就行了
obj.height = 177;
obj.weight = 67;

// 删
delete obj.height;
delete obj.weight;

注意:

这些属性都是该JS对象自身的属性,不包含继承而来的属性。

let obj = {
    'name': '张三',
    'sex': '男',
    'age': 11
};

Object.keys(obj);// ["name", "sex", "age"]
let obj = {
    'name': '张三',
};

// in法:可以判断JS对象是否包含某个属性,无论是它自身的还是继承的都算
'name' in obj;// true

// hasOwnProperty法:只会判断JS对象自身是否包含某个属性,继承的属性不算
obj.hasOwnProperty('name');// true

for-in可以用来遍历一个对象的全部属性,注意是全部哦,自身的和继承的属性都会遍历出来。

let obj = {
    'name': '张三',
    'sex': '男',
    'age': 11
};

// 和我们OC的for-in一样,只能遍历字典的key
for (let key in obj) {
    console.log(key);
    console.log(obj[key]);
}

但是通常情况下,我们只想遍历对象自身的属性,所以可以用for-inhasOwnProperty()结合来遍历。

let obj = {
    'name': '张三',
    'sex': '男',
    'age': 11
};

// 和我们OC的for-in一样,只能遍历字典的key
for (let key in obj) {
    if (obj.hasOwnProperty(key)) {// 判断一下这个属性是不是对象自身的属性
        console.log(key);
        console.log(obj[key]);
    }
}

for-of遍历要和数组的keys()values()entries()方法联合使用。keys()用来遍历key,values()用来遍历value,entries()用来同时遍历key和value。


// 遍历key
let obj = {
    'name' : '张三',
    'sex' : '男',
    age : 11
};
for (let key of Object. keys(obj)) {
    console.log(key);
}
// name
// sex
// age


// 遍历value
let obj = {
    'name' : '张三',
    'sex' : '男',
    age : 11
};
for (let value of Object.values(obj)) {
    console.log(value);
}
// 张三
// 男
// 11

// 遍历index和value
let obj = {
    'name' : '张三',
    'sex' : '男',
    age : 11
};
for (let [key, value] of Object.entries(obj)) {
    console.log(key, value);
}
// name 张三
// sex 男
// age 11
3.2 数组类

JS里的数组和OC里的数组基本一样,我们完全可以把JS里的数组当作OC里的可变数组来看待,也是用中括号[]表示。

但是也有点小区别,JS里的数组可以放任意数据类型的数据,而我们OC里的数组只能放对象类型的数据嘛。

let array = [
    true,
    11,
    '12',
    [11, '12'],
    {'girl': 11},
    {'boy': '12'},
    function () {console.log('sayHello');}
];

(1)数组的常用属性

length属性用来返回数组内元素的个数,和OC里的count比较像。

let array = [
    true,
    11,
    '12',
    [11, '12'],
    {'girl': 11},
    {'boy': '12'},
    function () {console.log('sayHello');}
];

array.length;// 7

但是也有点小区别,主要是因为JS里数组的下标可以是不连续的,所以length属性的真正意义其实等于数组最大下标值加1,而非数组内元素的个数,因此使用数组的length属性时一定要长点心,记住这个概念。

let array = [
    true,
    11,
    '12',
    [11, '12'],
    {'girl': 11},
    {'boy': '12'},
    function () {console.log('sayHello');}
];

array[11] = '嗨';// 下标可以不连续

array.length;// 不是8,而是12

通常情况下,我们会用length属性来清空数组,直接让数组的length属性等于0就可以了,对应OC的- removeAllObjects方法。

let array = [
    true,
    11,
    '12',
    [11, '12'],
    {'girl': 11},
    {'boy': '12'},
    function () {console.log('sayHello');}
];

array.length = 0;// 清空数组

array;// []

当然我们清空一个数组,也可以直接将数组置空,即arr = [];,但这种方式其实是把指针指向了一个新的数组对象,严格来说并不是将数组清空。

(2)数组的常用方法

push()方法用于在数组的尾部添加一个元素,并返回添加新元素后数组的长度,该方法会改变原数组。

let arr = [];

arr.push('a');// 返回数组长度,1
arr.push(['b', 'c']);// 返回数组长度,2

arr;// [a, ['b', 'c']]

以上我们已经看到了push()方法的基本用法,接下来对应到OC里,我们看一看它能干什么。

功能一:添加一个元素,对应OC的- addObject:方法。
功能二:批量添加元素——即从另一个数组批量添加元素,对应OC的- addObjectsFromArray:方法。

 let arr = [];

 arr.push('a');
 arr.push(...['b', 'c']);

 arr;// ["a", "b", "c"]

很遗憾,JS里想要删除元素只能通过下标删除(下面我们会提到),没有与- removeObject:- removeObjectsInArray:方法类似的直接实现,所以我们只能曲线救国,无论怎么绕最终都通过- removeObjectAtIndex:方法来删除。

pop()方法用于删除数组的最后一个元素,并返回删除掉的这个元素,该方法会改变原数组。

let arr = ['a', 'b', 'c'];

arr.pop();// 返回删除掉的元素,"c"

arr;// ["a", "b"]

以上我们已经看到了push()方法的基本用法,接下来对应到OC里,我们看一看它能干什么。

功能:移除最后一个元素,对应OC的- removeLastObject方法。

splice()方法用来删除数组中的一部分元素,同时可以在删除的位置新增元素,可见它同时有增删的功能欸,大有可为嘞,该方法会改变原数组。

// 第一个参数是要删除的元素的开始下标(删除时包含开始下标)
// 第二个参数是要删除的元素的长度
// 你想新增多少个元素就新增多少个元素,不是说你删了多少个,就只能新增多少个......
splice(开始下标, 个数, 要添加的元素1, 要添加的元素2, ...);
// 删除的 = 新增的
let arr = ['a', 'b', 'c', 'd', 'e', 'f'];
arr.splice(2, 3, 'cc', 'dd', 'ee');// 会移除掉"c"、"d"、"e",新增"cc"、"dd"、"ee"
arr;// ["a", "b", "cc", "dd", "ee", "f"]

// 删除的 > 新增的
let arr = ['a', 'b', 'c', 'd', 'e', 'f'];
arr.splice(2, 3, 'cc');
arr;// ["a", "b", "cc", "f"]

// 删除的 < 新增的
let arr = ['a', 'b', 'c', 'd', 'e', 'f'];
arr.splice(2, 3, 'cc', 'dd', 'ee', 'ccc');
arr;// ["a", "b", "cc", "dd", "ee", "ccc", "f"]

以上我们已经看到了splice()方法的基本用法,接下来对应到OC里,我们看一看它能干什么。不妨透漏一下,类似OC的插入、移除、替换都是通过这个方法来完成的。

功能一:在指定位置插入一个元素,对应OC的- insertObject:atIndex:方法。(第一个参数为要插入元素的下标,第二个参数固定为0代表不删除元素,第三个参数为要插入的元素。)

let arr = ['a', 'b', 'c', 'd', 'e', 'f'];

arr.splice(2, 0, '我是插入的元素哦');

arr;// ["a", "b", "我是插入的元素哦", "c", "d", "e", "f"]

功能二:移除指定位置的元素,对应OC的- removeObjectAtIndex:方法。(第一个参数为要移除元素的下标,第二个参数固定为1代表仅删除一个元素,后面不传新增元素就可以了。)

let arr = ["a", "b", "c", "d", "e"];

arr.splice(2, 1);

arr;// ["a", "b", "d", "e"]

功能三:用一个元素替换指定位置的元素,对应OC的- replaceObjectAtIndex: withObject:方法,其实替换一个元素的本质就是删除一个元素,然后在同样的位置上再新增一个元素,这完全就是splice()方法本色出演嘛。(第一个参数为要替换元素的下标,第二个参数固定为1代表仅删除一个元素,第三个参数为要替换成的元素。

let arr = ["a", "b", "c", "d", "e"];

arr.splice(2, 1, '我是替换的元素哦');

arr;// ["a", "b", "我是替换的元素哦", "d", "e"]

indexOf()方法用来获取某个元素的下标,第一次出现的哦,和OC的- indexOfObject:方法一模一样。

let arr = ['a', 'b', 'c'];

arr.indexOf('b');// 1

includes()方法用来判断数组中是否包含某个元素,和OC的- containsObject:方法一样。

let arr = ['a', 'b', 'c'];

arr.includes('b');// true

map()方法和forEach()方法很类似,都用来对数组的所有元素依次执行参数函数,但forEach()方法不需要返回值,map()方法需要返回值,也就是说forEach()方法的参数函数不能写返回值,而map()方法的参数函数必须写返回值。因此如果我们遍历数组的目的是依次操作数组元素并得到一组新的返回值,那就得使用map()方法,如果仅仅是为了依次操作数组元素,那就用forEach()方法。

map()方法首先需要一个函数作为它的参数,而且函数还必须得有返回值,然后它会把数组的所有元素依次传入参数函数执行,最后把每次执行的结果组成一个新数组作为返回值返回,原数组不受影响。

let arr = [1, 2, 3];
let resultArr = arr.map(function (num) {
    return num + 1;
});

arr;// [1, 2, 3]
resultArr;// [2, 3, 4]

forEach()方法也需要一个函数作为它的参数,但是函数不能有返回值,然后它会把数组的所有元素依次传入参数函数执行,执行完就执行完了,没有返回值,原数组不受影响。

let arr = [1, 2, 3];

arr.forEach(function (num) {
    console.log(num);
});

arr;// [1, 2, 3]

filter()方法用来过滤所有的数组元素。它也需要一个函数作为它的参数,而且函数还必须得有返回值,然后它会把数组的所有元素依次传入参数函数执行,最后把满足条件的元素组成一个新数组作为返回值返回,原数组不受影响。

let arr = [1, 2, 3, 4, 5];

let resultArr = arr.filter(function (num) {
    return num > 3;
});

resultArr;// [4, 5]

reverse()方法用来把数组逆向排序,并返回逆向排序后的数组,该方法会改变原数组。

let arr = ["a", "b", "c"];

arr.reverse();

arr;// ["c", "b", "a"]

join()方法用来将数组内的元素按指定的字符给连接起来,并返回连接后的字符串。

let arr = ["a", "b", "c"];
arr.join('@');// "a@b@c"

截取数组的一部分,同string的使用方法。

(3)数组的常用操作

这个也类似于我们OC里遍历数组。

for循环

let array = [
    true,
    11,
    '12',
    [11, '12'],
    {'girl': 11},
    {'boy': '12'},
    function () {console.log('sayHello');}
];

for (let i = 0; i < array.length; i ++) {
    console.log(array[i]);
}

// true
// 11
// 12
// Array(2)
// Object
// Object
// ƒ () {console.log('sayHello');}

for-in

JS里的数组也可以用for-in来遍历,但是这个和我们OC的for-in遍历数组有些区别,OC里我们用for-in遍历数组可以直接获取到数组的元素,而JS里用for-in遍历数组却只能获取到元素的下标。

这是什么原因呢?其实主要是因为for-in本来是人家JS对象的遍历方法,而JS里的数组刚好本质上也是一个JS对象(前面我们说过的),只不过所有的key是“0”、“1”、“2”、“3”......这样有序的排上去而已。

let array = [
    true,
    11,
    '12',
    [11, '12'],
    {'girl': 11},
    {'boy': '12'},
    function () {console.log('sayHello');}
];

for (let index in array) {// 遍历index
    console.log(array[index]);// 通过index获取相应的元素
}

// true
// 11
// 12
// (2) [11, "12"]
// {girl: 11}
// {boy: "12"}
// ƒ () {console.log('sayHello');}

for-of

for-of遍历要和数组的keys()values()entries()方法联合使用。keys()用来遍历index,values()用来遍历value,entries()用来同时遍历index和value。

// 遍历index
let arr = ['a', 'b', 'c'];
for (let index of arr.keys()) {
    console.log(index);
}
// 0
// 1
// 2


// 遍历value
let arr = ['a', 'b', 'c'];
for (let value of arr.values()) {
    console.log(value);
}
// "a"
// "b"
// "c"

// 遍历index和value
let arr = ['a', 'b', 'c'];
for (let [index, value] of arr.entries()) {
    console.log(index, value);
}
// 0 "a"
// 1 "b"
// 2 "c"
3.3 函数类

(1)函数的基本操作

函数,有声明、定义、调用三个基本操作。

这里解释一下这三个概念:

  • 声明:声明就是告诉编译器有这么一个函数,比如我们OC在.h文件里声明一个函数。
  • 定义:定义是指方法的具体实现,比如我们OC在.m文件里实现一个函数。
  • 调用:略......

在C语言里,如果一个函数的定义发生在该函数的调用之前,那么这个函数是不需要声明的。在JS里,因为所有函数的定义都会被提升到代码头部,所以我们不需要专门声明一个函数,直接定义和调用就可以了。

JS用function关键字来定义一个函数,函数可以分为普通函数和匿名函数。

普通函数,function关键字后面是函数名,函数名后面跟个小括号,里面放的是参数,然后就是一个大括号,是函数的执行体,如果函数有返回值的话,直接在函数体里返回就行了,函数不会事先表明有没有返回值以及返回什么类型的数据,如果函数没有返回值的话,执行体里不写返回值就好了。

普通函数的应用场景:

普通函数一般用来给类添加方法,只不过函数前面不需要再加function关键字了。那给类添加方法,我们一般有两个目的,一是要写个方法供类的内部或外界调用,二是把方法(或称函数)赋值给JSX控件的某个事件属性(如ButtononPress = {this.confirm})。

function sayHello(toSomeone) {
    console.log('look ' + toSomeone + ' in the eye, and say hello');

    return true;
}

匿名函数,它的定义和普通函数没什么大区别,只是没有名字而已,但是必须得有一个变量或属性来接收这个函数,也就是说匿名函数必须作为某个变量或属性的值而存在,它不能单独存在,因为它单独存在没有意义呀,将来你拿什么调用它呢,或者也可以把匿名函数直接作为另一个函数的参数也可以,否则会报错说“需要一个函数名”(function name expected)。

// 变量接收匿名函数
let sayHello = function (toSomeone) {
    console.log('look ' + toSomeone + ' in the eye, and say hello');
};

// 属性接收匿名函数
let person = {
    'sayHello': function (toSomeone) {
        console.log('look ' + toSomeone + ' in the eye, and say hello');
    }
};

// 匿名函数作为另一个函数的参数
let arr = [1, 2, 3];
let resultArr = arr.map(function (num) {
    return num + 1;
});
resultArr;// [2, 3, 4]

匿名函数的应用场景:

  • 立即执行的函数优先使用匿名函数。

  • 匿名函数更多的使用在作为另一个函数的参数或者称之为回调吧。

  • 另外,我们知道匿名函数类似于我们OC里的block,所以我们也可以跟着block的应用场景来考虑匿名函数,这个实际用到的时候再说,现在知道就可以了。

我们定义好函数之后,就可以调用它了,无论是普通函数还是匿名函数,调用的方式统一为函数名(参数)的方式。

// 普通函数
function sayHello(toSomeone) {
    console.log('look ' + toSomeone + ' in the eye, and say hello');

    return true;
}
sayHello('11');// look 11 in the eye, and say hello

// 匿名函数
let sayHello = function (toSomeone) {
    console.log('look ' + toSomeone + ' in the eye, and say hello');
};
sayHello('11');// look 11 in the eye, and say hello

// 匿名函数
let person = {
    'sayHello': function (toSomeone) {
        console.log('look ' + toSomeone + ' in the eye, and say hello');
    }
};
person.sayHello('11');// look 11 in the eye, and say hello

此外,函数也可以在定义完之后立马调用——即函数的立即调用,只不过这种方式对普通函数来说没什么意义,因为普通函数往往都是需要在好多地方重复调用的,而这种情况更适用于只有一个地方使用该函数的情况,因此匿名函数更适合。

// 普通函数也可以,但没什么大意义
(function sayHello() {
    console.log('hello');
})();// hello

// 更适合匿名函数
(function () {
    console.log('hello');
})();// hello

(2)函数的组成

函数,有函数名、参数、执行体、返回值四个部分组成。

name属性可以用来获取一个函数名的字符串格式。

function hello() {
}
hello.name;// "hello"

let hello = function () {
}
hello.name;// "hello"

如果函数的参数是基本数据类型,那么参数的传递方式为值传递(即深拷贝),函数内部修改参数的值不会影响到外面的值。

let num = 11;

function func(num) {
    num = 12;
}

func(11);

console.log(num);// 11

如果函数的参数是对象数据类型,那么参数的传递方式为指针传递(即浅拷贝),函数内部修改参数的值会影响到外面的值。

let nums = [11, 12];

function func(nums) {
    nums[0] = 13;
    nums[1] = 14;
}

func(nums);

console.log(nums);// [13, 14]

但请注意上面的情况,如果函数内部修改的不是对象某个属性的值,而是对整个对象进行修改,则不会影响外部的值。

let nums = [11, 12];

function func(nums) {
    nums = [13, 14];
}

func(nums);

console.log(nums);// [11, 12]

略......

略......

4. 特殊数据类型

nullundefined基本上没啥区别,我们就当它俩一样好了。

只不过一般情况下,我们会主动使用一下null,而undefined则是一种被动的状态,即我们给变量初始化的时候可能会赋值成nullundefined一般就代表一个变量我们声明了但是没有赋值。

5. 数据类型的强制转换

数据类型的强制转换主要是指使用Boolean()Number()String()三个函数,手动将各种类型的值,分别转换为布尔值、数值和字符串。

5.1 Boolean()函数

Boolean()函数可以把任意数据类型的值转换为布尔值。

它的转换规则相对简单:除了以下五个值会被转换为false,其它所有的值都会被转换为true

Boolean(0);// false
Boolean(NaN);// false
Boolean('');// false
Boolean(undefined);// false
Boolean(null);// false
5.2 Number()函数

Number()函数可以把任意数据类型的值转换为数值。

但是它的参数就要分情况来讨论了,一种是基本数据类型的值或特殊数据类型的值,一种是对象数据类型的值。

(1)参数为基本数据类型的值或特殊数据类型的值

// 布尔值:true转换为1,false转换为0
Number(true);// 1
Number(false);// 0

// 数值:转换后还是原来的值
Number(11.12);// 11.12

// 字符串:如果字符串可以被转换成数值,则转换成相应的数值
Number('11');// 11
// 字符串:如果字符串不可以被转换成数值,则返回NaN
Number('11hi');// NaN
// 字符串:如果是空字符串或者空格,则转换成0
Number('');// 0
Number(' ');// 0
// 字符串:会自动忽略字符串前后的空格
Number('    11    ');// 11

// null:转换成0
Number(null);// 0

// undefined:转换成NaN
Number(undefined);// NaN

(2)参数为对象数据类型的值

这个不常用吧,略......

5.3 String()函数

String()函数可以把任意数据类型的值转换为字符串。

它的参数也要分情况来讨论了,一种是基本数据类型的值或特殊数据类型的值,一种是对象数据类型的值。

(1)参数为基本数据类型的值或特殊数据类型的值

// 布尔值:true转换为字符串"true",false转换为字符串"false"
String(true);// "true"
String(false);// "false"

// 数值:转换为相应的字符串
String(11);// "123"

// 字符串:转换后还是原来的值
String('abc');// "abc"

// null:转为字符串"null"
String(null);// "null"

// undefined:转换为字符串"undefined"
String(undefined);// "undefined"

(2)参数为对象数据类型的值

这个不常用吧,略......

三. 运算符


1. 加法运算符

当然了,加法运算符的基本功能是求两个数值的和嘛。

1 + 1;// 2

但是在JS里,加法运算符还可以作为字符串的连接运算符。

'嗨' + '你好';// "嗨你好"

如果一个运算子是字符串,另一个运算子是非字符串,那么非字符串会转化为字符串,再连接起来。

false + 'hi';// "falsehi"
1 + '嗨';// "1嗨"

'3' + 4 + 5;// "345"
3 + 4 + '5';// "75"

JS的算术运算符里,除了加法运算符,其它的几个都只具备算数运算的功能,即便是遇到了别的数据类型也会把它们转化成数值来进行算数运算。

1 - '2';// -1
1 * '2';// 2
1 / '2';// 0.5
2. 严格相等运算符(===)和相等运算符(==

注意:

比较运算符可以比较各种类型的数据,不仅仅是数值类型。

JS里提供了两个相等运算符,严格相等运算符(===)和相等运算符(==),它俩的区别是相等运算符(==)比较的是两个数据的值是否相等,而严格相等运算符(===)比较的是两个数据是否是同一个东西。比如我们比较的两个数据不是同一个类型的,那严格相等运算符(===)会直接返回false,而相等运算符(==)会先将它们转换成同一类型,再用严格相等运算符进行一次比较。又比如我们比较的两个数据是同一个类型的,以复合数据类型为例好了,严格相等运算符(===)比较的还是它俩是否是同一个东西——即它俩是否指向同一块内存地址,而相等运算符比较的是它俩的内容是否一样。

因此,使用相等运算符(==)得到的结果有时会和我们的直觉不一样,这就容易导致代码出错,因此建议不要使用相等运算符(==),最好只使用严格相等运算符(===)。

上一篇下一篇

猜你喜欢

热点阅读