js内置的数据类型
- 基本类型:null、undefined、number、string、boolean、symbol
- 引用类型:object(array、function、date,regexp)
null
- 空对象指针,当定义将来要保存对象值的变量时,最好用null来初始化。
undefined
- 当未初始化变量时,变量会自动被赋值为undefined
boolean
- 2个值,区分大小写。
- 所有其它类型的值都有对应的布尔值的等价形式。
- 要将其它类型的值转换为布尔值,可以使用Boolean函数,转换规则如下:
| 数据类型 | 转换为true的值 | 转换为false的值 |
|---|---|---|
| Boolean | true | false |
| Number | 非零数值(包括无穷值) | 0、NaN |
| Object | 任意对象 | null |
| undefined | N/A(不存在) | undefined |
number
-
number用双精度值表示整数和浮点值。
-
不同的数值类型有不同的数值字面量格式:十进制、八进制(0或0o)、十六进制(0x)
-
如果字面量中包含的数字超出了有的范围,就会忽略前面的0,后面数字会被当成十进制数。比如08就是无效的八进制数,因为八进制的范围是0-7。所以08会被当成8处理。
-
浮点值:存储浮点值所用的空间是整数值的两倍,所以js会想法把值转化为整数,比如,忽略1.0后面的0。
-
科学计数法:表示较大或较小的数,例如3.12e5表示3.12*10^5。
-
js并不支持表示世界上所有的数值,无法表示的负数为-Infinity、Infinity。
-
要确定一个数是否位于js能表示的最小值与最大值之间,可以用isFinite()函数,它会返回一个布尔值。
-
NaN 9. 用于表示本来要返回数值的操作失败了,比如0除以任何数在js中都会得到NaN。而0作为分母时,则会得到-Infinity或Infinity的结果。 10. 所有涉及NaN的操作,都会返回NaN。 11.NaN不等于包括NaN在内的所有值。 12.isNaN()可以判断一个值是否为NaN。
-
将其它类型的值转换为数值类型
- Number()
类型 值 布尔值 true=1、false-0 数值 直接返回 null 0 undefined NaN 对象 调用valueOf()/toString(),会得到基本类型的值。 string 如果字符串包含数值字符,包括数值字符前面带加、减号的情况,则转换为一个十进制数值。因此Number("1")返回1,Number("123")返回123,Number("011")返回11(忽略前面的0)。 如果字符串包含有效的浮点值格式如1.1,则会转换为响应的浮点值,同样忽略前面的0 如果字符串包含有效的十六进制格式如"0xf",则会转换为与该十六进制值对应的十进制整数值 如果是空字符串,则返回0 如果字符串包含除上述情况外的其它字符,则返回NaN - parseInt()
- 在需要得到整数时,优先使用
- 转换时,最前面的0会被忽略,从第一个非空格字符开始转换,直到字符串末尾或碰到非数值字符(如果第一个数值不是数值/+/-,则立即返回NaN)
- 最终得到的是一个十进制数
- 可接受第2个参数n,表示把第一个参数作为n进制来识别。比如:把一个二进制数值转化成十进制,parseInt(1100,2) = 12。
- parseFloat()
- 始终忽略开头的0,且只能解析十进制数值
- 只能解析到字符串末尾或一个无效的浮点数值字符为止,第一次出现的小数点的有效的,第2次是无效的。
string
- 将一个值转换为字符串的方法
- toString(),可用于将数值、布尔值、字符串、对象和数值转换为字符串,null和undefined无此方法。
- String(),如果值有toString方法,就调用toString。
- 在字符串后面加一个空格
object
- constructor,用于创建当前对象的函数
- hasOwnProperty(),用于判断当前实例(不包含原型链)上是否存在指定属性,要检查的属性名必须是字符串。
- isPrototypeOf(),用于判断当前对象是否为另一个对象的原型
- toString(),返回对象的字符串表示
- valueOf(),返回对象的字符串/数值/布尔值表示,通常与toString()的返回值相同
- object是所有对象的基类,所以任何对象都有这些属性或方法,但有些方法可能会重写。
判断数据类型
- 使用typeof关键词
- typeof的原理:js底层在存储变量时,是用机器码的前3位存储类型信息的。比如000-对象,010-浮点数、100-字符串、110-布尔、1-整数。但是存储null和undefined时有些特殊,存储null时,它的机器码全是0,存储undefined时,是用-2^30。而typeof就是校验机器码的前三位来判断类型的。
- typeof是一个操作符,并不是一个函数,因此使用的时候无需用给函数传参的方式。
- 对一个值使用此操作符,会返回如下字符串之一:undefined、number、string、boolean、symbol、function、object。严格来讲,function并不是一种数据类型,但之所以能够判断出为function,是由于它自己的特殊性,所以把它和对象区分开来。
- 缺点:当值为object时,无法判断具体是哪一种object。
typeof null //object - 使用instanceof
- 原理:判断A的原型链是否在B的prototype上。因为每个js对象都有一个隐式__proto__,它指向构造函数的prototype。
- 无法判断基本类型,且用来判断数组的时候居多。
[] instanceof Array //true
//模拟实现简版instanceof
const myInstance = (left, Right) => {
if(typeof left !== 'object') {
return false;
};
while(true) {
if(left === null) {
return false;
};
if(Right.prototype === left.__proto__) {
return true;
};
left = left.__proto__;
}
}
- Object.prototype.toString.call
- 原理:Object原型上的方法是可以输出数据类型的。
- 按理说,js中所有的类都继承了Object,所以也继承了Object的toString()方法,那为什么不使用类上面的toString方法呢?因为所有的类在继承的时候都自己改写了toString方法,所以必须使用原型上的toString方法。
- 特点:万能方法,但是写法比较复杂。
Object.prototype.toString.call('jack')//[object String]
Object.prototype.toString.call(1)//[object Number]
- constructor
- 原理:所有 特点:不好用,因为如果是null/undefined的话,直接.constructor程序会报错。
var foo = 10;
console.log(foo.constructor);
js的隐式类型转换
JS 本身是一种弱类型的语言,我们不用提前声明好类型,程序运行时,类型会自动分配。
- 使用+运算符时的类型转换规则
- 当使用 + 运算符计算 string 其他类型相加时,都会转换成 string 类型。( 另外的值转成字符串 )
- 除以上情况外,会转成 number 类型,但是 undefined 会转成 NaN, 结果也是 NaN
- 当 + 号两边存在 NaN, 则结果为 NaN (Not a Number)
- 如果 Infinity + Infinity 为 Infinity, Infinity + (-Infinity) 为 NaN
- 如果 string 相加,直接拼接
- 如果 + 号两边,有一个是对象,则调用 valueOf() 或 toString()。具体调用哪个取决于内置函数toPrimitive。
- 其他情况,都会转换成 number 类型
ps:网红题目
can (a == 1 && a == 2 && a == 3) ever evalute to true?
解法:
const a = {
value: 1,
toString: function() {
return a.value ++
}
}
let value = 0;
Object.defineProperty(global, 'a', {
get: function() {
return ++value;
}
})
JS 的 引用与拷贝
- 基本的数据类型,保存在栈中(占用空间小、大小固定、频繁使用);
- 其他Array, Object, Function 都是 引用数据类型(占用空间较大,大小不固定);
Object.assign
特点:
- 值拷贝;
- 返回值为第一个参数;
- 浅层 merge;
const foo = {
a: 1,
c: {
d: 50
}
};
const bar = { a: 10, b: 20};
const baz = Object.assign(bar, foo);
foo.c = 200;
console.log(baz);//baz.c.d=50.
arr.slice
特点:拷贝的是地址。所以修改时,会相互影响。
const arr = [
{name:'luuuu'},
{name:'luuuu2'}]
const newArr = arr.slice(0,1)
arr[0].name 'zzzzzzz';
console.log(newArr)//[{name:'zzzzzzz'},{name:'luuuu2'}]
函数
定义方式
- function foo(){}
- const foo = function(){}
函数的调用方法
- 作为函数的调用
function foo() { console.log('foooo'); }; foo(); - 作为方法的调用
const bar = { foo() { console.log(`this is foo method of ${this.name}`) }, name: "BAZ" }; global.name = "GLOBAL"; const foo = bar.foo; foo(); // this is foo method of GLOBAL bar.foo(); // this is foo method of BAZ - 作为构造函数的调用
const Bar = function(name, fn) { this.name = name; this.handler = fn; }; const bar = new Bar('bar', function() { console.log(this.name); }); console.log(bar); bar.handler(); - 被call或者apply
const bar = {
// bar 这个对象,有个函数,叫做foo,可以打印对象调用者的名字和参数。
foo(param) {
console.log(`this is foo method of ${this.name},the param is ${param}`)
},
name: "BAR",
};
// 假如,我现在,有一个对象,叫做 obj, 这个 obj 想要使用这个 bar 的 foo 方法
// 但是我没有,我就只有“借用”。
const obj = { name: 'simple object' };
bar.foo.apply(obj, ['objParam'])
// foo -- frist object oriented
// bar -- binary arbitrary reason 任意二进制原因
方法链
const person = {
addAge: function(age) {
this.age = age;
return this;
},
addName: function(name) {
this.name = name;
return this;
},
addSkills: function(...skills) {
this.skills = skills.join(',');
return this;
},
print: function() {
console.log(`I am ${this.name}, I am ${this.age} years old. My skills are ${this.skills}`);
},
}
person.addName('luyi')
.addAge(35)
.addSkills('FE', 'Teaching')
.print();
实参和形参
形参
- JavaScript 语言本身,既没有给形参限制类型,也没有给形参做类型检查。
- JS 是没有重载的。
- 重载指一个类中可以有多个相同名称的方法,但是这些方法的参数不同。
- 形参是可选的,
- 我传入的实参,既可以比形参多,也可以比形参少。
- 当我的实参传入,少于形参的时候,按顺序走,多出来的,是 undefined;
function createPerson(name, age, isMale) {
// 要求,
return {
name, age, sex: isMale ? 'male' : 'female'
}
};
// console.log(createPerson('luyi', 35, true));
// console.log(createPerson('yunyin', true));
function createPerson2({ name, age, isMale }) {
// 要求,
return {
name, age, sex: isMale ? 'male' : 'female'
}
};
console.log(createPerson2({name: 'luyi', isMale: true }));
- 当我的实参传入,多于形参的时候
- arguments
- ...rest
function createPerson(name, ...rest) {
// 要求,
console.log(`${name}‘s messages is ${rest.join(', ')}`);
};
createPerson('luyi', 'a male', '35 years old', 'a FE', 'like reading and teaching');
function createPerson(name) {
// 要求,
// console.log(arguments);
// 伪数组
const rest = Array.prototype.slice.call(arguments, 1);
console.log(`${name}‘s messages is ${rest.join(', ')}`);
};
createPerson('luyi', 'a male', '35 years old', 'a FE', 'like reading and teaching');
实战: 写一个无限加法
function add(...rest) {
return rest.reduce((total, item) => total + item, 0);
}
function add2() {
return Array.prototype.reduce.apply(arguments, [(total, item) => total + item, 0]);
}
console.log(add(1,2,3,4,5));
console.log(add2(1,2,3,4,5));
高阶函数:函数是一等公民
1. 函数作为值
// 函数可以作为值
function square(x) { return x * x };
const a = square;
console.log(square(2));
console.log(a(2));
2. 函数作为参数
function calculate(param, cb) {
setTimeout(() => {
const res = param * param - param;
cb(res);
}, 500);
};
calculate(234, (res) => {
console.log(`after 500ms, the result is ${res}`);
});
3. 函数作为返回值
function discount(ratio) {
let _ratio = ratio;
return function(price) {
console.log(_ratio * price);
};
};
const discount75 = discount(0.75);
discount75(100);
discount75(100);
discount75(100);
discount75(100);
discount75(100);
实战:防抖与截流
// luyi
// luyi // 300ms 你没有在输入,
// 防抖与节流
function debounce(fn, delay = 300) {
// 默认 300 毫秒
let timer;
return function() {
const args = arguments;
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
}
};
function foo() {
console.log('foo');
}
const de_handleChange = debounce(handleChange, 300);
de_handleChange();
de_handleChange();
dfoo();
// throttle
function throttle(fn, delay) {
let flag = true;
return () => {
if(!flag) return;
flag = false;
setTimeout(() => {
fn();
flag = true;
}, delay);
}
};
function foo() {
console.log('foo');
}
const tfoo = throttle(foo, 300);
tfoo();
tfoo();
tfoo();
setTimeout(() => {
tfoo();
tfoo();
tfoo();
tfoo();
tfoo();
},400);