JS 基础

97 阅读6分钟

数据类型

基本类型

  • 有6种:string、boolean、number、symbol(符号)、null、undefined
  • string、boolean、number、symbol 这四种被称为原始类型
  • symbol 表示独一无二的值,通过 Symbol 函数调用生成,由于生成的 symbol 值为原始类型,而new调用生成的object对象,所以 Symbol 函数不能使用 new 调用
const s = Symbol();//不能使用 new 操作符
console.log(typeof s); // "symbol"

//Symbol 方法接收一个参数,表示对生成的 symbol 值的一种描述
const s = Symbol('foo');

//即使是传入相同的参数,生成的 symbol 值也是不相等的,因为 Symbol 本来就是独一无二的意思
const foo = Symbol('foo'); 
const bar = Symbol('foo'); 
console.log(foo === bar); // false
  • null 和 undefined 通常被认为是特殊值,这两种类型的值唯一,就是其本身。
  • boolean:以下五个值为false :undefined、null、0、NaN、''(空字符)
  • undefined: 出现undefined 情况
//1.一个变量声明,但未赋值,输出则是undefined;
let a; // undefined

//2.一个function函数中,没有显式return返回值,就默认的返回值是undefined
function test() {
   console.log('dddd');
} // 默认返回值是 undefined

//3.函数实参数小于形参数,那么多余的形参是默认是undefined(其实就是该形参还未被赋值)
function say(name,message){
    console.log('Hello' + name + message);
}
say('World!’);
//控制台打印出:HelloWorld!undefined

引用类型

  • 只有 object 对象类型是引用类型.是无序的集合,存放各种值的容器
  • arrayfunction是对象的子类型
  • 包装类型: string是基础类型,但是为什么会有属性和方法呢。其实在引用字符串的属性或方法时,会通过调用 new String() 的方式转换成对象,引用结束,又会销毁这个临时对象;
typeof 'tester' // 'string' 
typeof new String('tester') // 'object'

不仅仅只是字符串有包装对象的概念,数字和布尔值也有相对应的 new Number() 和 new Boolean() 包装对象。null 和 undefined 没有包装对象,访问它们的属性会报类型错误。

类型判断

  • typeof
typeof 'seymoe' // 'string' 
typeof true // 'boolean' 
typeof 10 // 'number' 
typeof Symbol() // 'symbol' 
typeof null // 'object' 无法判定是否为 null 
typeof undefined // 'undefined'

根据以上可以看出,只有 null 的判定会有误差。

typeof {} // 'object' 
typeof [] // 'object' 
typeof(() => {}) // 'function'

只使用typeof 类型并不能判断具体的子类型,上面的 [] {} 就都是object类型

  • instanceof 通过 instanceof 操作符也可以对对象类型进行判定,其原理就是测试构造函数的 prototype 是否出现在被检测对象的原型链上。
[] instanceof Array // true 
({}) instanceof Object // true 
(()=>{}) instanceof Function // true
  • Object.prototype.toString() js中终极判定数据类型, 具体用法如下
Object.prototype.toString.call({}) // '[object Object]' 
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call(() => {}) // '[object Function]'
Object.prototype.toString.call('test') // '[object String]'
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(Symbol()) // '[object Symbol]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call(Math) // '[object Math]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(new WeakSet()) // '[object WeakSet]'
Object.prototype.toString.call(new Map()) // '[object Map]'
Object.prototype.toString.call(new WeakMap()) // '[object WeakMap]'

该方法本质就是依托Object.prototype.toString() 方法得到对象内部属性 [[Class]]; 传入原始类型却能够判定出结果是因为对值进行了包装; null 和 undefined 能够输出结果是内部实现有做处理

NaN

NaN 是一个全局对象的属性,NaN是一种特殊的Number类型。

  • 什么时候返回NaN
Infinity / Infinity;   // 无穷大除以无穷大
Math.sqrt(-1);         // 给任意负数做开方运算
'a' - 1;               // 算数运算符与不是数字或无法转换为数字的操作数一起使用
'a' * 1;
'a' / 1;
parseInt('a');         // 字符串解析成数字
parseFloat('a');

Number('a');   //NaN
'abc' - 1   // NaN
undefined + 1 // NaN
//一元运算符(注意点)
+'abc' // NaN
-'abc' // NaN

该段引用
玩转JavaScript之数据类型
JS数据类型
面试官问道的JavaScript数据类型

赋值、深|浅拷贝

  • 赋值:赋的其实是该对象的在栈中的地址,而不是堆中的数据,即指针的引用
  • 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响
  • 深拷贝:内存中开辟一块新的区域,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响
// 对象赋值
let obj1 = {
    name : '浪里行舟',
    arr : [1,[2,3],4],
};
let obj2 = obj1;
obj2.name = "阿浪";
obj2.arr[1] =[5,6,7] ;
console.log('obj1',obj1) // obj1 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj2',obj2) // obj2 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }

// 浅拷贝
let obj1 = {
    name : '浪里行舟',
    arr : [1,[2,3],4],
};
let obj3= Object.assign({}, obj1); //浅拷贝
obj3.name = "阿浪";
obj3.arr[1] = [5,6,7] ; // 新旧对象还是共享同一块内存
console.log('obj1',obj1) // obj1 { name: '浪里行舟', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj3',obj3) // obj3 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }

// 深拷贝
var _ = require('lodash');

let obj1 = {
    name : '浪里行舟',
    arr : [1,[2,3],4],
};
let obj4= _.cloneDeep(obj1);; //深拷贝 这里使用lodash.cloneDeep 
obj4.name = "阿浪";
obj4.arr[1] = [5,6,7] ; // 新对象跟原对象不共享内存
console.log('obj1',obj1) // obj1 { name: '浪里行舟', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj4',obj4) // obj4 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }

浅拷贝实现方式

  • Object.assign()
let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' }; 
let obj2 = Object.assign({}, obj1);
  • 函数库lodash的_.clone方法
var _ = require('lodash'); 
var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; 
var obj2 = _.clone(obj1); 
console.log(obj1.b.f === obj2.b.f);// true
  • 展开运算符... 与 Object.assign ()的功能相同。
let obj1 = { name: 'Kobe', address:{x:100,y:100}} 
let obj2= {... obj1}
  • Array.prototype.concat() 用于连接两个或多个数组 arrayObject.concat(arrayX,arrayX,......,arrayX)
let arr = [1, 3, { username: 'kobe' }]; 
let arr2 = arr.concat();
  • Array.prototype.slice() 从已有的数组中返回选定的元素 arrayObject.slice(start,end)
let arr = [1, 3, { username: ' kobe' }]; 
let arr3 = arr.slice();

深拷贝实现方式

  • JSON.parse(JSON.stringify()) 利用JSON.stringify将对象转成JSON字符串,再用JSON.parse把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝
let arr = [1, 3, { username: ' kobe' }]; 
let arr4 = JSON.parse(JSON.stringify(arr));

这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。 eg:

let arr = [1, 3, { username: ' kobe' },function(){}]; 
let arr4 = JSON.parse(JSON.stringify(arr)); 
arr4[2].username = 'duncan'; 
console.log(arr, arr4)

  • 函数库lodash的_.cloneDeep
var _ = require('lodash'); 
var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; 
var obj2 = _.cloneDeep(obj1);
  • jQuery.extend() jquery 有提供一個$.extend可以用来做 Deep Copy
$.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝

eg:
var $ = require('jquery');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false

引用浪里行舟 - 浅拷贝与深拷贝

Window

window.open()方法会直接打开新的窗口显示跳转网页,是window对象的方法。
window.location.assign() 会在当前网页更改url的地址进行跳转,不会弹出新的页面,是location对象的属性。

<input type="button" value="新窗口打开" onclick="window.open('http://www.baidu.com')">
<input type="button" value="当前页打开" onclick="window.location='http://www.baidu.com/'">

JSX

JSX在编译时会被Babel编译为React.createElement方法

to be continue...

m: Model = {} & m = {} as Model

interface Model {
  name: string;
  like: string:
}

const m: Model = {
  name: '', 
  like: '',
 }; 
const m1 = {} as Model;
console.log(m);  //{name: '', like: ''}
console.log(m1); // {}
console.log(m1.name); // undefined