JS-数据类型
类型
TIP
基本数据类型:Number,String,Boolean,Undefined,Null
引用数据类型:Object,Array,Function,RegExp,Date,Error
全局数据类型:Math
弱类型语言: javascript 是弱类型语言,在声明时是不需要定义类型的,变量的类型就是其值的类型;
缺点:在不确定的地方就会发生类型转换,无法确定类型,必须要掌握类型转换的原理;
检测数据类型
typeof
TIP
对一个值使用了 typeof 操作符,会返回以下某个字符串
- 'undefined' //如果这个值未定义
- 'boolean' // 如果是布尔值
- 'string' // 如果是字符串
- 'number' // 如果是数字
- 'object' // 如果值是对象或 null
- 'function' // 如果是函数;
例子:
var message = 'hello'
console.log(typeof message) // 'string'
console.log(typeof 95) // 'number'var message = 'hello'
console.log(typeof message) // 'string'
console.log(typeof 95) // 'number'typeof 作符的操作数可以是变量,也可以是字面量
typeof 不能准确判断变量是什么类型,null 会返回 object,因为 null 的定义是空对象指针;
常使用的环境:
判断函数是否有传值:typeof val == 'undefined';val 是形参;
判断函数传值的类型:typeof val == 'number';
因为检测数据类型时,输入的结果是字符串,所以需要加引号;typeof (typeof a) 检测传入的 a 的类型的类型;
instanceof 检测数据类
TIP
- 检测前面的变量是不是某个类型,是的话返回 true,不是返回 false 如果是 new 出来的函数,可以使用这个判断。
- 检测类型时类型首字母需要大写;
- 检测基本数据类型时需要 new 实例化,复杂数据类型不需要 new 实例化;必须要 new Number()
- 数组看是不是 Object 对象时,返回值是 true,但是 Array 大类的;
示例:console.log(obj instanceof Object);
console.log(123 instanceof Number) // false 必须new实例化
console.log([] instanceof Array) // trueconsole.log(123 instanceof Number) // false 必须new实例化
console.log([] instanceof Array) // truetoString.call
理解:
- Object.prototype.toString.call, call 前面的代码就是 this 指向;
- 因为 bind 是返回绑定的函数, 因此将 toString 函数放进去作为第一个参数;
- 由于 call 需要一个方法来调用, 因此将 Function.prototype 作为前缀;
改造:
types('') // [object String]; 直接使用types来判断;
const t = (type) => Function.prototype.call.bind(Object.prototype.toString)(type).slice(8, -1)types('') // [object String]; 直接使用types来判断;
const t = (type) => Function.prototype.call.bind(Object.prototype.toString)(type).slice(8, -1)constructor 判断(扩展)
TIP
console.log({}.constructor === Object) // true
通过constructor判断的,Math.constructor === Math //false,在javascript中,没有Math的构造函数,Math的构造函数在Object上,可以使用toString.call来判断;console.log({}.constructor === Object) // true
通过constructor判断的,Math.constructor === Math //false,在javascript中,没有Math的构造函数,Math的构造函数在Object上,可以使用toString.call来判断;不推荐此方法,因为
var a = [1, 2, 3]
a.constructor === Array //truevar a = [1, 2, 3]
a.constructor === Array //true如果 a=null,则报错;
使用原型链判断是否是类型的原型
reg.__proto__ === RegExp.prototypereg.__proto__ === RegExp.prototypeUndefined 类型
Undefined 类型只有一个值,即特殊的 undefined,
在使用 var 声明变量时,没有赋值,就是 undefined;
函数没有明确返回值,如果接受了,结果是 undefined;
注意
即使不声明,使用 typeof 也会返回'undefined';
var name
console.log(typeof name) // 'undefined'
console.log(typeof age) // 'undefined'var name
console.log(typeof name) // 'undefined'
console.log(typeof age) // 'undefined'Null 类型
Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null;
null 表示一个空对象指针,因此检测 null 的数据类型时,会返回 object;
如果需要保存的值类型为对象,建议赋值为 null;
注意
判断 undefined == null 时,结果为 true,因为 undefined 值是派生自 null 值的,无论在什么情况下都没有必要把一个变量的值显式地设置为 undefined;
面试题
undefined 和 null 的区别
- null 表示值为空,但是是 object 对象,
- undefined 表示变量没有初始化,值为空缺;
- null ==undefined // 因为都为空,不对比类型;
- null === undefined //false 类型不同
null 是对象吗?
- null 的返回值是 object,但是 null 不是对象,而是基本数据类型的一种;
Boolean 类型
boolean 类型是使用最多的类型,只有两个值:true(真)和 false(假),在 ECMAScript 中是区分大小写的,True 和 False 都不是 boolean 类型,只是标识符;
boolean 类型的转换规则
| 数据类型 | 转换 true 的值 | 转换 false 的值 |
|---|---|---|
| Boolean | true | false |
| String | 任何非空字符串 | 空字符串 |
| Number | 任何非零数值 | 0 和 NaN |
| Object | 任何对象 | null |
| Undefined | 不适用转换 true | undefined |
Number 类型
- 最基本的数值字面量格式是十进制整数
- 八进制字面值第一位必须是 0 且后面的值必须小于 8,否则还是十进制
- 十六进制以 0x 开头,十六进制一般用在表示颜色上
浮点数值
在表示 0.1 时,通常可以直接.1,去掉 0,浮点数值的最高精度是 17 位小数,0.1 加 0.2 的结果不是 0.3,而是 0.300000000000000004,这是使用基于 IEEE754 数值的浮点计算的通病,所有语言如此,永远不要测试某个特点的浮点数值;
数值范围
Number.MIN_VALUE 最小值,这个值是 5e-324,
Number.MAX_VALUE 最大值,这个值是1.7976931348623157e+308Number.MAX_VALUE 最大值,这个值是1.7976931348623157e+308Infinity 无穷大 -Infinity 无穷小,isFinite() 判断是还是无穷数;
e 的表示法:3.125e7 == 31250000,实际是 3.125 乘以 10 的 7 次幂;0.0000003 表示 3e-7;
NaN
(1)即非数值,是一个特殊值,任何数值除以非数值会返回 NaN,不会影响其他代码的执行;
(2)NaN 与任何值都不相等,NaN 不等于 NaN;
(3)判断是不是 NaN,使用 isNaN,在判断时会尝试将这个值转换为数值再进行判断;isNaN 在对对象调用时,会首先调用对象的 valueOf 方法,然后再确定该方法返回的值是否可以转换为数值,如果不能,则基于这个返回值再调用 toString()方法;
isNaN(NaN) //true
isNaN(10) //false
isNaN('10') //false
isNaN('blue') //true
isNaN(true) // false (true被转换为1)isNaN(NaN) //true
isNaN(10) //false
isNaN('10') //false
isNaN('blue') //true
isNaN(true) // false (true被转换为1)数值的转换
Number()、
parseInt()、parseFloat(),第一个函数可以用于任何数据类型,另外两个函数则专门用于把字符串转换成数值;Number 转换规则
如果是 boolean 值,true 为 1,false 为 0
如果是数字,直接返回传入的数值;
如果是 null,返回 0
如果是 undefined,返回 NaN,特殊值
如果是字符串,遵循以下规则
- 如果字符串只包含数字,'1'转换 1,'011'转换为 11;
- 如果包含有效浮点格式,如:'1.1',则将转换对应的浮点数值;
- 如果字符串包含有效十六进制,则将其转换相同大小的十进制
- 如果字符串是空的,转换为 0
- 如果字符串除以上规则,转换为 NaN;
如果是对象,则调用对象的 valueOf 方法,如果转换结果是 NaN,则调用对象的 toString 方法;
Number('hello') // NaN
Number('') // 0
Number('00011') //11
Number(true) // 1Number('hello') // NaN
Number('') // 0
Number('00011') //11
Number(true) // 1注意:空的数组转换为 0,空的对象转换为 NaN; 一般在需要转换的值前面带上+符,可以直接转换,+[],转换为 0;
- parseInt 转换规则
由于 Number()函数在转换字符串时比较复杂而且不够合理,因此在处理整数的时候更常用的是 parseInt 函数;
parseInt 会忽略空格,直到找到第一个非空格字符,如果第一个不是数字或者负号,则返回 NaN;
parseInt 在转换查找时,如果第一个是数字,则会继续解析第二个字符,直到后续字符遇到一个非数字字符,如:'12px'转换为 12;
在遇到小数点时,也不会解析,需要使用 parseFloat;
parseInt 转换空字符串时,返回 NaN,Number 转换空字符串返回 0;
parseInt 有第二个参数,以什么进制来转换;
- 如:由于 16 进制是以 0x 开头的,如果没有 0x,'af'转换为 NaN,如果是 parseInt('af',16),则转换是 175;
- '070'是以 0 开头,转换是以 8 进制,则是 56,如果是 parseInt('070',10),则是 70;
- parseFloat 转换规则
- 主要是解决 parseInt 不能识别小数点而诞生的;
- parseFloat 只解析 10 进制,如果是 0xA,则解析是 0;
额外的方法
toFxied(n),小数点后保留多少位;toExponential(n),返回指数表示法(也称 e 表示法);toPrecision(n),根据要处理的数值决定使用 toFxied 还是 toExponential;
var num = 99
alert(num.toPrecision(1)) //"1e+2"
alert(num.toPrecision(2)) //"99"
alert(num.toPrecision(3)) //"99.0"var num = 99
alert(num.toPrecision(1)) //"1e+2"
alert(num.toPrecision(2)) //"99"
alert(num.toPrecision(3)) //"99.0"``|0 可以直接取整,比如:12.3|0 结果是 12;
String 类型
- String 类型用于表示由零或多个 16 位 Unicode 字符组成的字符序列,字符串可以由双引号(""),或单引号('')表示
- 字符串的长度可以使用 length 属性来读取
- 转换为字符串:
- 使用 toString()方法转换,可以传入的参数有 2-36 之间,一般传入 2. 8. 10. 16
- String()转换规则
- 如果值有 toString()方法,则调用该方法并返回相应的结果
- 如果值是 null 或 undefined,则返回'null'或'undefined'
注意:
null和undefined值没有toString()这个方法,需要使用String()来转换- 整数直接
.toString()会报错,可以添加小括号转换; (1).toString() - 如果是对象
.toString(),转换成[object Object]
BigInt 类型
TIP
BigInt 是 JavaScript 中一种内置对象,用于表示大于 2^53 - 1 的整数(即 Number 能安全表示的最大整数)。
- 创建方式:在整数后面加
n或调用BigInt()函数 - BigInt 不能与 Number 直接混合运算(需显式转换)
- 支持
+,-,*,**,/,%运算符(除法结果会截断小数)
// 创建 BigInt
const big1 = 9007199254740993n
const big2 = BigInt(9007199254740993)
const big3 = BigInt('9007199254740993')
// 运算
console.log(big1 + 2n) // 9007199254740995n
console.log(big1 * 2n) // 18014398509481986n
console.log(big1 / 3n) // 3002399751580331n(截断小数)
// 与 Number 比较
console.log(1n == 1) // true(宽松相等)
console.log(1n === 1) // false(严格相等)
console.log(1n < 2) // true(可以比较)
// 混合运算需显式转换
// console.log(big1 + 1) // TypeError
console.log(big1 + BigInt(1)) // 正确
console.log(Number(big1) + 1) // 正确,但有精度丢失风险// 创建 BigInt
const big1 = 9007199254740993n
const big2 = BigInt(9007199254740993)
const big3 = BigInt('9007199254740993')
// 运算
console.log(big1 + 2n) // 9007199254740995n
console.log(big1 * 2n) // 18014398509481986n
console.log(big1 / 3n) // 3002399751580331n(截断小数)
// 与 Number 比较
console.log(1n == 1) // true(宽松相等)
console.log(1n === 1) // false(严格相等)
console.log(1n < 2) // true(可以比较)
// 混合运算需显式转换
// console.log(big1 + 1) // TypeError
console.log(big1 + BigInt(1)) // 正确
console.log(Number(big1) + 1) // 正确,但有精度丢失风险注意
- BigInt 不支持单目运算符
+ - BigInt 不能用于
Math对象的方法 - 转为 Number 时若超出安全范围会丢失精度
复杂数据类型(引用类型)
引用类型的特点
- 复杂数据类型有Object(对象)、Array(数组)、function(函数)
- 引用类型任何数据类型都可以储存
- 引用类型的地址存储在堆里面,当别的值修改了原始值之后,都会被修改
- 基本类型存储的是值,引用类型存储的是地址(指针),复制引用类型时只会复制内存指针
引用类型的拷贝
浅拷贝
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝:基本类型拷贝的是值,引用类型拷贝的就是内存地址;所以当我们操作新对象中的引用类型时会影响源对象
Object.assign()
const obj1 = {
name: 'xiaoming',
props: { a: 1 },
}
const obj2 = Object.assign({}, obj1)
obj2.name = '小明'
obj2.props.a++
obj1 // { name: 'xiaoming', props: { a: 2 } }
obj2 // { name: '小明', props: { a: 2 } }const obj1 = {
name: 'xiaoming',
props: { a: 1 },
}
const obj2 = Object.assign({}, obj1)
obj2.name = '小明'
obj2.props.a++
obj1 // { name: 'xiaoming', props: { a: 2 } }
obj2 // { name: '小明', props: { a: 2 } }Array.prototype.concat()
const arr1 = [1, 2, 3, [4, 5]]
const arr2 = arr1.concat()
arr2[0] = 'arr2'
arr2[3][0] = 'arr2'
arr1 // [1, 2, 3, ['arr2', 5]];
arr2 // ['arr2', 2, 3, ['arr2', 5]];const arr1 = [1, 2, 3, [4, 5]]
const arr2 = arr1.concat()
arr2[0] = 'arr2'
arr2[3][0] = 'arr2'
arr1 // [1, 2, 3, ['arr2', 5]];
arr2 // ['arr2', 2, 3, ['arr2', 5]];Array.prototype.slice()
const arr1 = [1, 2, 3, [4, 5]]
const arr2 = arr1.slice()
arr2[0] = 'arr2'
arr2[3][0] = 'arr2'
arr1 // [1, 2, 3, ['arr2', 5]];
arr2 // ['arr2', 2, 3, ['arr2', 5]];const arr1 = [1, 2, 3, [4, 5]]
const arr2 = arr1.slice()
arr2[0] = 'arr2'
arr2[3][0] = 'arr2'
arr1 // [1, 2, 3, ['arr2', 5]];
arr2 // ['arr2', 2, 3, ['arr2', 5]];ES6 扩展运算符
/* 对象 */
const obj1 = {
name: 'xiaoming',
props: { a: 1 },
}
const obj2 = { ...obj1 }
obj2.name = '小明'
obj2.props.a++
obj1 // { name: 'xiaoming', props: { a: 2 } }
obj2 // { name: '小明', props: { a: 2 } }
/* 数组 */
const arr1 = [1, 2, 3, [4, 5]]
const arr2 = [...arr1]
arr2[0] = 'arr2'
arr2[3][0] = 'arr2'
arr1 // [1, 2, 3, ['arr2', 5]];
arr2 // ['arr2', 2, 3, ['arr2', 5]];/* 对象 */
const obj1 = {
name: 'xiaoming',
props: { a: 1 },
}
const obj2 = { ...obj1 }
obj2.name = '小明'
obj2.props.a++
obj1 // { name: 'xiaoming', props: { a: 2 } }
obj2 // { name: '小明', props: { a: 2 } }
/* 数组 */
const arr1 = [1, 2, 3, [4, 5]]
const arr2 = [...arr1]
arr2[0] = 'arr2'
arr2[3][0] = 'arr2'
arr1 // [1, 2, 3, ['arr2', 5]];
arr2 // ['arr2', 2, 3, ['arr2', 5]];深拷贝
深拷贝是将一个对象从内存中完整的拷贝一份出来,即从堆内存中开辟一个新的区域存放新对象,所以修改新对象不会影响原对象
JSON.parse(JSON.stringify())
const obj1 = {
name: 'xiaoming',
props: { a: 1 },
}
const obj2 = JSON.parse(JSON.stringify(obj1))
obj2.name = '小明'
obj2.props.a++
obj1 // { name: 'xiaoming', props: { a: 1 } }
obj2 // { name: '小明', props: { a: 2 } }const obj1 = {
name: 'xiaoming',
props: { a: 1 },
}
const obj2 = JSON.parse(JSON.stringify(obj1))
obj2.name = '小明'
obj2.props.a++
obj1 // { name: 'xiaoming', props: { a: 1 } }
obj2 // { name: '小明', props: { a: 2 } }JSON.parse(JSON.stringify()) 存在明显的弊端:
- 只能序列化对象的可枚举的自有属性
undefined、Symbol、任意函数将被忽略NaN、Infinity、-Infinity将被当成null处理RegExp、Error、Set、Map等特殊对象,仅会序列化可枚举的属性(一般情况下即为空对象)Date类型,转换后会调用toJSON转为字符串类型- 循环引用的对象将报错
const map = new Map()
map.set(1, 2) // Map: 0: {1 => 2}
const obj1 = {
a: undefined,
b: null,
c: Symbol(),
d: NaN,
e: Infinity,
f: -Infinity,
g: map,
h: new Date(),
i: () => {},
}
Object.defineProperty(obj1, 'j', {
value: 'string',
})
const obj2 = JSON.parse(JSON.stringify(obj1))
/** 源对象 obj1
{
a: undefined,
b: null,
c: Symbol(),
d: NaN,
e: Infinity,
f: -Infinity,
g: Map(1) {1 => 2}
h: Fri Mar 10 2023 22:41:08 GMT+0800 (中国标准时间) {},
i: () => {},
j: 'string'
}
**/
/** 新对象 obj2
{
b: null,
d: null,
e: null,
f: null,
g: {},
h: '2023-03-10T14:41:08.110Z'
}
**/const map = new Map()
map.set(1, 2) // Map: 0: {1 => 2}
const obj1 = {
a: undefined,
b: null,
c: Symbol(),
d: NaN,
e: Infinity,
f: -Infinity,
g: map,
h: new Date(),
i: () => {},
}
Object.defineProperty(obj1, 'j', {
value: 'string',
})
const obj2 = JSON.parse(JSON.stringify(obj1))
/** 源对象 obj1
{
a: undefined,
b: null,
c: Symbol(),
d: NaN,
e: Infinity,
f: -Infinity,
g: Map(1) {1 => 2}
h: Fri Mar 10 2023 22:41:08 GMT+0800 (中国标准时间) {},
i: () => {},
j: 'string'
}
**/
/** 新对象 obj2
{
b: null,
d: null,
e: null,
f: null,
g: {},
h: '2023-03-10T14:41:08.110Z'
}
**/structuredClone
HTML规范标准的 Web API
const original = { name: 'MDN' }
original.itself = original
const clone = structuredClone(original)
console.assert(clone !== original) // the objects are not the same (not same identity)
console.assert(clone.name === 'MDN') // they do have the same values
console.assert(clone.itself === clone) // and the circular reference is preservedconst original = { name: 'MDN' }
original.itself = original
const clone = structuredClone(original)
console.assert(clone !== original) // the objects are not the same (not same identity)
console.assert(clone.name === 'MDN') // they do have the same values
console.assert(clone.itself === clone) // and the circular reference is preservedHTML 规范的标准提案,使用结构化克隆算法将给定的值进行深拷贝,支持循环引用。还可以使用 structuredClone(value, { transfer }) 调用方式使可转移对象仅被传递,不被克隆(直接移动源数据)
注意点
尽管作为规范标准实现的 Web API,但目前兼容性还是个巨大的问题,同时仍有其他不足:
- 无法拷贝对象的原型链
- 无法拷贝函数
- 不支持
Error数据类型
MessageChannel
vue.nextTick源码曾使用的Web API,在了解这个API时发现可以用于深拷贝
function cloneUsingChannel(obj) {
return new Promise((resolve) => {
const channel = new MessageChannel()
channel.port1.onmessage = (e) => resolve(e.data)
channel.port2.postMessage(obj)
})
}function cloneUsingChannel(obj) {
return new Promise((resolve) => {
const channel = new MessageChannel()
channel.port1.onmessage = (e) => resolve(e.data)
channel.port2.postMessage(obj)
})
}但该方法存在一个缺陷,当拷贝对象带有函数属性时,将抛出错误:
const obj1 = {
fn: function () {},
}
const obj2 = cloneUsingChannel(obj1)
// Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'MessagePort': function () {} could not be cloned.const obj1 = {
fn: function () {},
}
const obj2 = cloneUsingChannel(obj1)
// Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'MessagePort': function () {} could not be cloned.JQuery.extend()
import $ from 'jquery'
const obj2 = $.extend(true, {}, obj1)import $ from 'jquery'
const obj2 = $.extend(true, {}, obj1)lodash.cloneDeep
import { cloneDeep } from 'lodash-es'
const obj2 = cloneDeep(obj1)import { cloneDeep } from 'lodash-es'
const obj2 = cloneDeep(obj1)object(对象)
TIP
- 以花括号的形式为对象
- 万物皆对象,页面中的任何一个元素都是对象,包括 document,window,对象就相当于地球
- 创建对象的三个方式
- 实例化方式:var 变量=new Object();输出是一个空的花括号,表示对象
- 字面量:var 变量 = {}
- Object.create(null) 也可以创建一个新对象
- 对象中以键值对表示存储值,键名:键值;也可以叫属性名:属性值;多个键名之间用逗号隔开
- 设置对象的元素值:对象名.键名 = 键值; 一般称作键值对
- 获取值的方法:① 变量名.键名;② 变量名[""]
Array(数组)
TIP
- 创建方式
- 实例化:var 变量 = new Array();以中括号形式为数组,一个值表示数组的长度,多个值表示数组的数据如果是一个引号引起来的,则是单个数组
- 字面量:var 变量 = [ ]
- 数组的长度是下标加一
- 数组的长度可读可写,可以删除数组的数据
- 数组不能用 typeof 来判断是不是数组,判断的是 object,判断是不是数组可以用 Array.isArray(参数)来判断,返回结果是布尔型;IE9 以上支持判断
- 数组可以储存所有类型,也可以存储数组和对象
- 设置数组:数组名[索引] = 值
- 获取数据:数组名[索引]
面试题
var ary = [], ary[0]=1, ary[5]=5var ary = [], ary[0]=1, ary[5]=5数组的长度是 6,没有声明的为undefined;
function 函数
function (); 事件处理函数;空格后面可以设置函数名- 调用函数:函数();加小括号是直接执行,事件不需要加小括号
关于 Javascript 中数字的部分知识总结
- Javascript 中,由于其变量内容不同,变量被分为基本数据类型变量和引用数据类型变量。基本类型变量用八字节内存,存储基本数据类型(数值、布尔值、null 和未定义)的值,引用类型变量则只保存对对象、数组和函数等引用类型的值的引用(即内存地址)
- JS 中的数字是不分类型的,也就是没有 byte/int/float/double 等的差异
- 基本类型:null,undefined,number,boolean;string 比较特殊!!! 引用类型 Obeject,function,array
计算机最小的计量单位是 bit,翻译为位
8bit = 1byte,byte 翻译为字节
所以 32bit = 4 byte
八字节为 64 位
基本包装数据类型
javascript 中有三个基本包装数据类型:String、Number、Boolean;
TIP
每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据,其实 String、Number、Boolean 三种类型的值在读取的时候由于是基本包装类型(也可以称是引用类型),因此有属性和方法,可以使用点操作符,读取不到的属性返回 undefined,而 undefind 和 null 两种类型会直接报错
基本包装数据类型的特征
- 可以使用 new 操作符,当使用 new 操作符之后的类型使用 typeof 检测之后是 object,但是可以使用 instanceof 来检测;
使用 new 和不使用 new 的特点
TIP
引用类型与基本包装类型的主要区别就是对象的生存期。使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。
堆和栈
- 基本数据类型,储存在栈里面,两个基本数据类型是相等的
- 引用数据类型,地址存储在栈里面,数据存在堆里面,调用时先从栈里获取地址,再从堆中获取数据,两个复杂数据类型是不相等的;堆里面反复存数据会导致内存占满
new 操作符
new 操作符创建出来的一定是一个空对象
如果在构造函数里面,显式写了一个返回值,会发生的情况?
如果返回的不是一个对象,相当于没写,会被忽略,也就是说如果在构造函数中显式的返回了一个不是对象的数据类型,最后得到的还是对象,如果是对象,会把设置的对象返回出来
function Car(name, age) {
this.name = name
return { age: age }
}
let car = new Car('lishi', 18)
car.name //undefined 因为返回没有name;
car // {age:18} 并且不能标识具体谁创建的;
// 第二种情况,返回不是对象;
function Car(name, age) {
this.name = name
return age
}
let car = new Car('lishi', 18)
car // Car {name: 'lishi'} return 没有起作用;function Car(name, age) {
this.name = name
return { age: age }
}
let car = new Car('lishi', 18)
car.name //undefined 因为返回没有name;
car // {age:18} 并且不能标识具体谁创建的;
// 第二种情况,返回不是对象;
function Car(name, age) {
this.name = name
return age
}
let car = new Car('lishi', 18)
car // Car {name: 'lishi'} return 没有起作用;- 使用 new 操作符执行了哪些过程
- 创建一个空对象
- 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
function create() {
var obj = new Object()
var fn = [].shift.call(arguments) //将第一个函数截取;
obj.__proto__ = fn.prototype
var result = fn.apply(obj, arguments)
return result instanceof Object ? result : obj
//一般new一个函数,函数可以填写return,如果return了就得将函数返回值保存并返回,函数必须是对象,上面第2点已经讲过;
}
// or
function create(fn, ...args) {
const obj = new Object()
obj.__proto__ = fn.prototype
const result = fn.apply(obj, args)
return result instanceof Object ? result : obj
}
// 使用实例
function fn(age) {
this.age = age
}
let obj1 = create(fn, 18) // fn{age:18}
let obj2 = new fn(18) //fn{age:18}
console.log(obj1.__proto__ === obj2.__proto__) //truefunction create() {
var obj = new Object()
var fn = [].shift.call(arguments) //将第一个函数截取;
obj.__proto__ = fn.prototype
var result = fn.apply(obj, arguments)
return result instanceof Object ? result : obj
//一般new一个函数,函数可以填写return,如果return了就得将函数返回值保存并返回,函数必须是对象,上面第2点已经讲过;
}
// or
function create(fn, ...args) {
const obj = new Object()
obj.__proto__ = fn.prototype
const result = fn.apply(obj, args)
return result instanceof Object ? result : obj
}
// 使用实例
function fn(age) {
this.age = age
}
let obj1 = create(fn, 18) // fn{age:18}
let obj2 = new fn(18) //fn{age:18}
console.log(obj1.__proto__ === obj2.__proto__) //true增进问题
在使用 new 操作符时,如果没有参数,括号是可以省略的
运算符
js 赋值运算符是从右往左的,var a = 1,把 1 的值赋给 a; '.'是优先级最高的运算符
数字运算符
- + - * / %(取余) ++(自增) --(自减)
- 号遇到字符串变成拼接
- ++(--) 在前,先运算,后赋值
- ++(--) 在后,先赋值,后运算
var i=1;
j=i++ + i++;
j=1 + 2++;
j=3;
var a=1;
b=++a + ++a;
b=2 + ++2;
b= 5;var i=1;
j=i++ + i++;
j=1 + 2++;
j=3;
var a=1;
b=++a + ++a;
b=2 + ++2;
b= 5;赋值运算符
- = += -= *= /= %=
- a+=2 为 a=a+2
- 一个等号为赋值
- 赋值运算表达式:由赋值运算符链接起来的叫赋值表达式
比较运算符
- > >= < <= !=(不等于) !==(绝对不等于) ==(等于) ===(绝对等于)
- '!=' 不等于只看值是否相同,不看类型
- '!==' 绝对不等于除了看值不同,还看类型是否不同
- 比较运算符的输出结果是 boolear 类型
- "two">"three"//结果为 true "tw"在字母表中的索引大于"th"
逻辑运算符
- &&(与--并且) ||(或---或者) !(非--取反或取非)
- && 只要有一个为假,就为假
- || 只要有一个为真,就为真
- !表达式: 表达式 1 的结果是 true,整个结果为 false
- 表达式 1 的结果是 false,整个结果为 true
- (!"")为 true 空字符串为假,取非就为真
连等运算符
a=b=c 为 b=c 是 a=b;永远取等号右边的表达式结果赋值到等号的左侧;
var a = { n: 1 }
var b = a
a.x = a = { n: 2 }
console.log(a.x) // undefined;var a = { n: 1 }
var b = a
a.x = a = { n: 2 }
console.log(a.x) // undefined;理解:
var a = {n:1}
var b = a
a = {n:2} // 这里 a 指向了新的地址,b 还是指向原来的地址,b 没有重新被赋值;
上题中点的优先级最高,首先 a.x = a,赋值了,a 又被重新赋值,因此 b.x 有值;a.x 为新对象为 undefined;var a = {n:1}
var b = a
a = {n:2} // 这里 a 指向了新的地址,b 还是指向原来的地址,b 没有重新被赋值;
上题中点的优先级最高,首先 a.x = a,赋值了,a 又被重新赋值,因此 b.x 有值;a.x 为新对象为 undefined;位运算
位运算适用于 32 位整数,所以精度可能会丢失
用'|'取整
let num = 1.5
num = num | 0 // 1let num = 1.5
num = num | 0 // 1用>>取半
let num = 4
num = num >> 1 //2let num = 4
num = num >> 1 //2用<<加倍
let num = 2
num = num << 2 //4let num = 2
num = num << 2 //4用'^'两值交换
let a = 1,
b = 2
a ^= b
b ^= a
a ^= b
console.log(a, b) // 2 1;let a = 1,
b = 2
a ^= b
b ^= a
a ^= b
console.log(a, b) // 2 1;用&判断奇数
let n = 3
let m = 4
n & (1 === 1) // true 奇数
m & (1 === 1) //false 偶数let n = 3
let m = 4
n & (1 === 1) // true 奇数
m & (1 === 1) //false 偶数用'~'判断项是否存在
~的特性是:操作数的负值-1;~12 为-13;
let firstname = 'ma'
let fullname = 'jack ma'
~fullname.indexOf(firstname) // truelet firstname = 'ma'
let fullname = 'jack ma'
~fullname.indexOf(firstname) // true由于负数-1,找不到是-1,-1 就是 0 了
用'+'快速转换数字类型;
console.log(typeof +'11') //numberconsole.log(typeof +'11') //number如果+号前面有数字则会成拼接符;
用 ** 表示平方
let n = 4 ** 4 // 256let n = 4 ** 4 // 256隐式转换
- 在进行计算时,会隐式转换;10+true 为 11;,10-true 为 9;
- 当+号遇到字符串的时候,+变成拼接符,将前后拼接起来,多个相加时,碰到引号开始拼接;
- 隐式转换会首先调用 toString 方法,如果没有则调用 valueOf 方法;
- undefined 和谁进行运算都是 NaN
- 两个+会是 NaN: 'a'+ + 'b' aNaN 后面这一个 b 转`换为 NaN
上题相当于 'a'+(+'b')
理解:由于+是一元正号,计算操作数的数值时,会进行 Number 转换:Number('b') 转换为 NaN,剩下的就是字符串和其他类型相加,都会被转化为字符串;
例子:
let date = new Date() + date // 将进行Number(date)转换,转换成时间戳let date = new Date() + date // 将进行Number(date)转换,转换成时间戳- {}空对象和谁进行运算都自动转换 toString,为[object Object];
function sum(x) {
console.log('两个数的和为' + x)
}
function sum(x, y) {
console.log('两个数的和为' + (x + y)) //NaN;
}
sum(10)
//y没有传值,10+undefined; 为NaN undefined是字符串类型,+号遇到字符串时隐式转换了;
// 如果没有(x+y)的小括号,则会是10undefined,成拼接符;function sum(x) {
console.log('两个数的和为' + x)
}
function sum(x, y) {
console.log('两个数的和为' + (x + y)) //NaN;
}
sum(10)
//y没有传值,10+undefined; 为NaN undefined是字符串类型,+号遇到字符串时隐式转换了;
// 如果没有(x+y)的小括号,则会是10undefined,成拼接符;json
json 是一种数据交换格式,从后台请求下来的数据基本上都是 json 类型,
json 数据用{}括起来,里面的数据以 key:value 存储。
Cao Note