web前端 -- js 判断数据类型方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web前端 -- js 判断数据类型方法相关的知识,希望对你有一定的参考价值。

参考技术A

1、typeof 操作符

typeof 目前能返回string,number,boolean,unfined,object,function,symbol,bigint,这八种判断类型。使用方式:typeof(表达式)和typeof 变量名,具体判断如下:

特别注意数组array的typeof 操作符判断

2、instanceof
A instanceof B 可以判断A是不是B的实例,返回一个布尔值,由构造类型判断出数据类型,目前支持数组,对象,date,function类型。
注: instanceof 后面一定要是对象类型,大小写不能写错!!!

亲测: Symbol is not a constructor

3、Object.prototype.toString.call()
通过Object下的toString.call()方法来判断,目前是最为可靠的类型检测手段,它会将当前对象转换为字符串并输出。但它也不是完美的,它无法检测用户自定义类型。 因为Object.prototype是不知道用户会创造什么类型的, 它只能检测ECMA标准中的那些 内置类型

注意, 这里的Object和function判断打印都是[object Object]。使用Object.prototype.toString.call判断Symbol报错。

4、contructor
依据 对象 的contructor判断,返回一个布尔值。
注: ===后面一定要是对象类型,大写且不能写错!!!

前端笔记整理(JS)

前端基础知识

数据类型

JS数据类型

  • 基本类型:number、string、null、symbol、boolean、undefined
  • 对象类型:Object(Array、RegExp、Math、Map等)、Function

不可以使用 var col = [1,2,] 这样可能会创建2个或者3个长度的数组,因浏览器不同

数组

检测数组

  • ES5新方法:Array.isArray(arr)
    兼容写法:
	function isArray(arr)
		return Object.prototype.toString.call(arr) == "[Object Array]"
	
  • 只有一个全局执行环境时:arr instanceof Array

迭代方法

  • every((item,index,array)=>):所有的数组项都符合判断时返回true,否则返回false;
  • some((item,index,array)=>:只要数组项其中一项符合判断时返回true,否则返回false;
  • filter((item,index,array)=>:对数组项进行过滤,然后将符合条件的数组项添加到一个新的数组,返回新数组;
  • map((item,index,array)=>:遍历且返回执行函数后的结果组成的新数组,返回新数组;
  • forEach((item,index,array)=>:仅遍历,不进行返回;

转换方法

  • toString() 返回以逗号拼接的数组各项值的字符串
  • toLocalelString()
  • join(",") 返回以指定字符串拼接的数组各项值的字符串

栈方法(后进先出)

  • push() 接收任意数量的参数,逐个添加至数组末尾,返回修改后的数组的长度
  • pop() 移除数组末尾最后一项,返回移除的项

队列方法(先进先出)

  • shift() 移除数组中的第一项并返回该项
  • unshift() 接收任意数量的参数,逐个添加至数组前端,返回新数组长度

重排序方法

  • reverse():反转数组顺序
  • sort():按升序排列数组项(sort()会调用每个数组项的toString()方法,比较字符串
    sort()可接受一个比较函数作为参数,优化比较
	var arr = [1,5,2,9,11,6];
	arr.sort(function(a,b)
   	return a-b
   )
   console.log(arr)   //[11,9,6,5,2,1]

操作方法

  • concat(a1,a2) 复制一个副本,并且向当前副本添加接收到的参数,可接受任意类型参数(除Object) ,返回新构建的副本
  • slice(start,end) 接收一个或者2个参数。用于截取数组参数,并返回截取到的项组成的新数组
  • splice(start,num,...args) 接收至少2个参数,返回删除的项组成的数组(没有删除时返回[]),用途:
    • 删除:arr.splice(0,2) —— 删除数组前两项
    • 插入:arr.splice(2,0,"dsdg","svsdfg") —— 删除项数量为0,从当前数组的2位置开始添加2项:“dsdg”,“svsdfg”
    • 替换:arr.splice(2,1,"123") —— 向指定位置插入任意数量的项,且同时删除任意数量的项

位置方法(全等比较)

  • indexOf(value[,index]):从数组起始位置查找,返回项在数组中的位置索引
  • lastIndexOf(value[,index]):从数组末尾位置开始查找,返回项在数组中的位置索引

其它:
- ES6中find(value):返回第一个符合传入测试(函数)条件的数组元素;
- findIndex(value):返回符合传入测试(函数)条件的数组元素索引;
- includes(value):判断一个数组是否包含一个指定的值。

归并方法

  • reduce((pre,cur,index,array)=>):从第一项开始迭代
  • reduceRight((pre,cur,index,array)=>):至少一项返回true,则返回true从最后一项开始迭代
    函数返回的任何值都会自动传递给下一项。第一次迭代发生在数组的第二项上
	var values = [1,3,5,8,9];
	var sum = values.reduce(function(prev,cur,index,arr)
		return prev+cur
	)
	console.log(sum)  //26

类数组(Array-like)对象

slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。你只需将该方法绑定到这个对象上。 一个函数中的 arguments 就是一个类数组对象的例子。

function list() 
  return Array.prototype.slice.call(arguments);


var list1 = list(1, 2, 3); // [1, 2, 3]

简化使用:[].slice.call(arguments)

对象

  • for(let i in obj)

属性类型

ECMAScript有两种属性:数据属性访问器属性

  1. 数据属性(包含一个数据值的位置):
  • [[Configurable]] :是否可修改属性特性、删除属性、把属性修改为访问器属性(默认为true
  • [[Enumerable]] :是否可以通过for-in循环返回属性(默认为true
  • [[Writable]]:是否可以修改属性值(默认为true
  • [[Value]]:包含这个属性的数据值(默认为undefined

修改属性特性,需要使用ECMAScript5的Object.definedProperty(属性所在对象,属性名,描述符对象)方法,其中描述符必须是数据属性之一。设置一个或者多个值,可以修改对应的特性值
[[Configurable]]设置为false后,则调用Object.definedProperty()方法也无法修改

  1. 访问器属性:
  • [[Configurable]] :是否可修改属性特性、删除属性、把属性修改为访问器属性(默认为true
  • [[Enumerable]] :是否可以通过for-in循环返回属性(默认为true
  • [[Get]]:在读取属性时调用的函数(默认为undefined
  • [[Set]]:在写入属性时调用的函数(默认为undefined

访问器属性不能直接定义,必须使用Object.definedProperty()定义

读取属性的特性(ECMAScript5)

Object.getOwnPropertyDescriptor(属性所在对象,描述符名称)可以取得给定属性的描述符

String类型

  1. 字符串方法:
  • str.charAt(index) 返回给定位置的字符
  • str.charCodeAt(index) 返回给定位置的字符的字符编码
  • ES5中可以使用str[index]访问指定位置的字符
  1. 字符串操作方法
  • concat() 字符串拼接
  • substr(start,num) 返回从起始位置截取num个字符(参数为负值时,-start+字符串长度,-num=0)
  • substring(start,end) 返回从起始位置到结束位置的字符(参数为负值时,全部转为0)
  • slice(start,end) 返回从起始位置到结束位置的字符(参数为负值时,负值+字符串长度进行转换)
  1. 字符串位置方法
  • indexOf(str,start)lastIIndexOf(str,start) 返回字符在字符串中的位置
  1. trim()方法(ES5新提供)—— 删除字符串首尾空格
  2. 字符串大小写转换
  • toLowerCase
  • toUpperCase
  1. 字符串的模式匹配方法
  • match(type) match只接收一个正则或者RegExp对象
    返回数组:
  • serach(type) search只接收一个正则或者RegExp对象
    返回字符串中第一个匹配项的索引
  • replace(type,str|function)
  • split(str,length) 基于一个分隔符将字符串分割成多个字符串并存于一个数组中,可以指定数组长度

类型判断

  • typeof :基本类型的数据中除了null,其它类型都可以通过typeof判断;对于对象类型来说,除function以外,其它类型判断的值均为object;
  • instanceof :通过原型链的方式来判断是否为构建函数的实例,常用于判断具体的对象类型;
  • Object.prototype.toString.call(v) ;
  • isXXX API :isArray、isNaN;
typeof null // object
[] instanceof Array // true
Object.prototype.toString.call(null) // "[object Null]"
Array.isArray([]) // true;  
isNaN(',') // true

nullundefined的区别

  • null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN

  • undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:

    • 变量被声明了,但没有赋值时,就等于undefined
    • 调用函数时,应该提供的参数没有提供,该参数等于undefined
    • 对象没有赋值的属性,该属性的值为undefined
    • 函数没有返回值时,默认返回undefined
  • null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。典型用法是:

    • 作为函数的参数,表示该函数的参数不是对象
    • 作为对象原型链的终点。

0.1+0.2 !== 0.3

JS中浮点数用二进制表示的时候是无穷的,因为精度问题,两个浮点数相加会造成截断丢失精度,因此再转换成十进制就会出问题。

手写instanceof

function myInstanceof(obj,pro)
	obj = obj.__prpto__;
	pro = pro.prototype;
	while(obj)
		if(obj === pro) return true;
		obj = obj.__proto__;
	
	return false;

typeof和instanceof类型判断

  • typeof可以准确判断除了null的基本类型,null和对象都会返回object
  • instanceof能准确判断对象的类型,对于基本类型都返回false,内部机制根据原型链来判断,如果沿着A的原型链,同时沿着B的原型链来找,如果能找到同一个引用,就返回true。

类型转换

  • 强制转换:转换成特定的类型(Number()、 v.toString())
    • 转换Boolean规则:undefined、null、false、NaN、0,都转换为false;其它都为true,包括所有对象;
    • 转换Number规则:true为1,false为0;null为0,undefined为NaN ,symbol报错;字符串是数字或者进制值就正常转,否则NaN ,对象隐式转换;
  • 隐式转换:valueOf()、toString() (只有当加法运算符时,有字符串类型则统一转字符串类型,其它只要有数字类型,就都转数字);
[]==![] // -> ?      输出为true
  1. == 和===的区别
  • ==, 两边值类型不同的时候,要先进行类型转换,再比较
  • ===,不做类型转换,类型不同的一定不等;
  1. 特殊数据类型解析
  • Number()、parseIn(value, 基数)、parseFloat()
  • toString(基数)、String()
  • Object:
    • constructor 保存用于创建当前对象的函数
    • hasOwnProperty(name) 检测给定属性在当前对象实例中是否存在
    • isPrototypeOf(object) 检测传入的对象是否是当亲对象的原型
    • propertyIsEnumerable(name) 检查给定的属性是否能够使用 for-in枚举
    • toLocalString() 范湖对象的字符串表示
    • toString()
    • valueOf() 返回对象的字符串、数值或者布尔值表示

toString()和valueof()的区别

  • toString()方法返回一个表示该对象的字符串。如果是对象返回,toString()会返回’[object type]’,其中type就是对象类型;
  • valueof()方法返回指定对象的原始值。JS会利用 valueof()把对象转换为原始类型的值(数值、字符串、布尔值)。
var array = ['aa','bb','cc'];
console.log(array.toString()); //aa,bb,cc
console.log(array.valueOf());//['aa','bb','cc']

深浅拷贝

引用类型复制:将存储在变量对象中的值(指针,指向存储在堆中的一个对象)复制到新变量分配的空间上。复制完成,两个变量将引用同一个对象

  • 两个对象第一层的引用不相同就是浅拷贝的含义。可以通过assign、扩展运算符等方式来实现浅拷贝;
  • 两个对象内部所有的引用都不相同就是深拷贝的含义。可以通过递归的方式解决;
//深拷贝
//1. JSON.stringfy(obj)

//2. 递归

function deepClone(data)
	let type = Object.prototype.toString.call(data);
	let obj;
	if(type === '[object Array]')
		obj = [];
		for(let i=0;i<data.length;i++)
			obj.deepClone(data[i]);
		
	else if(type === '[object Object]')
		obj = ;
		for(let i in data)
			obj[i] = deepClone(data[i]);
		
	else 
		return obj
	
	return obj

变量提升

var声明的变量存在声明提升情况,即提升到变量所在作用域的顶端执行,再到变量位置进行赋值操作;ES6中let声明变量不存在声明提升情况

function test () 
    console.log(a);  //undefined
    var a = 123; 
;

a = 1;
var a;
console.log(a); //1

函数提升

函数提升存在两种方式,即:

  • 函数声明式;该情况是将函数整个代码块提升到它所在的作用域最开始执行;
  • 函数字面量式;同var声明类似,该声明函数只是一个具体的值。

var、let、const的区别

  • var声明的变量,其作用域为该语句所在作用域内,存在变量提升现象;
  • let 声明的变量,其作用域为该语句所在函数内,不存在变量提升现象,存在暂时性死区;且相同作用域下,不可重复定义;
  • const声明的变量声明时必须立即赋值,且不允许修改;

ES6箭头函数

特点:

  • 箭头函数 this为父作用域的 this,不是调用时的this
  • 箭头函数不能作为构造函数,不能使用new
  • 箭头函数么有arguments、caller、callee;
  • 箭头函数通过call和apply 调用,不能改变this的指向,只会传入参数;
  • 箭头函数没有原型属性;
  • 箭头函数返回对象时,要加一个小括号

函数

函数是对象,函数名是指向函数对象的指针。一个函数可以有多个函数名。
函数定义方式:

  • 函数声明 (存在函数声明提升
  • 函数表达式 变量 = 匿名函数

参数

  1. arguments 类数组对象,主要用于保存参数
    arguments 的属性 callee:指针,指向拥有这个arguments对象的函数,递归调用的时候使用arguments.callee(),可以避免因为函数名重复时造成的bug
  2. this 引用的是函数执行的环境对象(全局作用域调用函数时,this引用的是window对象)
  3. callerES5 规范的函数对象属性,保存调用当前函数的函数的引用,可可以使用arguments.callee.caller
    严格模式下,访问arguments.callee()会报错;不能为caller赋值

函数调用过程中,值传递、参数不对等处理上,不管实参的数目大于还是小于形参,数据类型是否准确,调用的函数都会被执行
理解:JS中的参数在内部是用数组表示的。函数接收的始终是个数组(arguments对象),可以通过下标来获取传入的每一个元素(arguments[0]),也可以用length属性来气确定传递进来的参数个数

	function hello(name,msg)
		console.log("hello ' + arguments[0] +arguments[1])
		console.log(arguments.length)
	
	function say()
		console.log("hello ' + arguments[0] +arguments[1])
		console.log(arguments.length)
	
	hellow('lili',',how are you');
	say('lili',',how are you');
	//以上都会输出:hello lili ,how are you

返回值

函数要么有返回值要么没有返回值,否则容易造成调试不便。
未指定返回值的函数返回的是一个特殊值undefined

属性和方法

  1. 每个函数都包含两个非继承而来的方法:apply()call(),两者作用相同,仅仅是接收参数的格式不同
	fun.apply(obj,[...arguments])
	fun.call(obj,...arguments)

作用:

  • 用于在特定的作用域中调用函数,即重新定义函数体内this对象的值
  • 扩充函数作用域,对象不需要与方法有任何的耦合关系,函数中操作使用this指向
  1. ES5 中新定义方法:bind() 创建一个函数的实例,其this值会绑定到传给bind()函数的值

函数重载

     函数没有签名(接收的参数类型和数量),所以ECMAScript没有函数重载。定义相同名称的函数,只会执行最后一个。

递归

定义:一个函数通过名字调用自身

  1. arguments.callee()可以解决函数重定义情况
    arguments.callee()是一个指向正在执行的函数的指针,可用来代替函数名,因此可以用来实现递归调用
  2. 严格模式下arguments.callee()访问时会报错,可用命名函数表达式解决
var factorial = (function f(num)
	if(num < 1)
	return 1
	else
	return num*f(num -1)
)

原型链

this

this对象是指运行时基于函数的执行环境决定的,指向调用函数的对象。全局函数中,this指向window;当函数作为某个对象的方法调用时,this指向那个对象;匿名函数的执行环境具有全局性,其this对象指向window。

  • call()、apply()、bind()会改变this指向,优先级仅次于new;
  • 箭头函数:箭头函数没有this。箭头函数中,this只取决于定义时的环境。
var a = 1
const fn = () => 
  console.log(this.a)

const obj = 
  fn,
  a: 2

obj.fn()  //1

const a = 
  b: 2,
  foo: function ()  console.log(this.b) 

function b(foo) 
  // 输出什么?
  foo()

b(a.foo)  //undefined

内存泄漏

内存泄漏:指任何对象在不再拥有或需要它之后仍然存在;
操作:

  • setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
  • 闭包、控制台日志、死循环(在两个对象彼此引用且彼此保留时,就会产生一个循环
  • 过度递归
  • 无效的全局变量

闭包

  • 定义:假如一个函数能访问外部的变量,那么这个函数就是一个闭包。闭包常用来访问私有变量。
  • thisargument对象:与外层函数无关,只与调用自己时的对象以及传的参数有关。
  • 特性:
    • 函数嵌套函数
    • 函数内部可以引用外部的参数和变量
    • 参数和变量不会被垃圾回收机制回收
for (var i = 0; i < 6; i++) 
  setTimeout(() => 
    console.log(i)
  )
  //输出6  解决办法:let定义i
  1. 数据存放在堆中还是栈中?栈是有结构的,先进后出,数据查询速度:栈>堆
    JS中基本类型值在内存中固定大小,保存在栈内存中;引用类型是对象,保存在堆内存中;JS在定义变量时就分配了内存,使用时候就是对内存的读写操作,内存释放依赖于浏览器的垃圾回收机制;

  2. 垃圾回收

JS每次创建字符串、对象、数组等,解释器都必须动态分配内存来存储实体。这种动态分配了内存的,最终都要释放这些内存一遍他们能够再利用,否则系统内可用内存会被消耗完,造成系统崩溃。

常用回收方法

  • 标记清除;
  • 引用计数
  1. 闭包的优缺点
  • 优点
    • 读取私有变量;
    • 可封装对象的私有属性和私有方法;让这些变量时装保存在内存中;
  • 缺点
    • 占用更多内存;滥用闭包可能会造成网页性能问题,在IE中可能导致内存泄漏(解决办法:退出函数之前,将不适用的局部变量删除

new

  • new操作费、符创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
  • 属性和方法被加入到this 引用的对象中。
  • 新创建的对象由 this 所引用,并且最后隐式的返回 this
//手写实现一个new
// 构造器函数
let Parent = function (name, age) 
    this.name = name;
    this.age = age;
;
Parent.prototype.sayName = function () 
    console.log(this.name);
;
//自己定义的new方法
let newMethod = function (Parent, ...rest) 
    // 1.以构造器的prototype属性为原型,创建新对象;
    let child = Object.create(Parent.prototype);
    // 2.将this和调用参数传给构造器执行
    let result = Parent.apply(child, rest);
    // 3.如果构造器没有手动返回对象,则返回第一步的对象
    return typeof result  === 'object' ? result : child;
;
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';

作用域

定义:变量的可访问性。
分类:

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6中的let、const)

作用域链:作用域的嵌套。往上遍历查找。

问:箭头函数和普通函数的区别

  • 箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()
  • 普通函数的this指向调用它的那个对象

作用域链

作用域链本质上是一个指向变量对象的指针列表,只是引用但不实际包含变量对象。

模仿块级作用域

JavaScript没有块级作用域的概念。js可多次声明同一变量,可多次执行初始化。匿名函数可以用来模仿块级作用域,限制向全局作用域中添加过多的变量和函数

	;(function()
		//块级作用域
	)();

原型

原型是什么?

  • 所有对象都有一个属性 __proto__指向一个对象,也就是原型;
  • 每个对象的原型可以通过 constructor找到构造函数,沟站函数也可以通过prototype找到原型;
  • 对象之间通过__proto__连接起来,这样称之为原型链;当前对象上不存在的属性可以通过原型链一层层往上查找,直到顶层Object对象,最后就是null

继承

即使是ES6中的class也不是其它语言中的类,本质就是一个函数。

class Person ;
Person instanceof Function  //true

ES6和ES5中的继承的区别:

  • ES6 继承的子类需要调用 super() 才能拿到子类,ES5 的话是通过 apply 这种绑定的方式
  • 类声明不会提升,和 let 这些一致
  1. JS 如何实现继承
    JS实现继承主要是依靠原型链。主要有6种继承方式:
  • 原型链:利用原型让一个引用类型继承另外一个引用类型的属性和方法;
  • 借用构造函数:在子类型构造函数的内部调用超类构造函数,通过使用call()和apply()方法可以在新创建的对象上执行构造函数;
function SuperType() 
	this.colors = ["red","blue","green"];

function SubType() 
	SuperType.call(this);//继承了SuperType


var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//"red","blue","green","black"

var instance2 = new SubType();
console.log(instance2.colors);//"red","blue","green"
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合市继承
  1. 通过原型实现的继承和class有什么区别?

类从类继承并创建子类关系;原型是一个工作对象实例。对象直接从其他对象继承。

单体内置对象

Global对象

所有在全局作用域中定义的属性和函数,都是Global对象的属性和方法

  1. URI 编码方法
    encodeURI()encodeURIComponent()decodeURI()decodeURIComponent()
  2. evval(str)方法

eval() 类似一个JavaScript解析器,只接收一个参数(要执行的javascript字符串)

  • eval()中定义的变量和函数都不会被提升;只在eval()执行的时候创建
  • 严格模式下,在外部无法访问eval()中定义的任何变量或者函数
  • 使用eval(),存在代码注入的恶意攻击危险
  1. window对象
    在全局作用域中声明的变量和函数,都成为了window对象的属性

Math对象

  1. min()max()方法
    可接收任意多个数值参数。求数组中

    以上是关于web前端 -- js 判断数据类型方法的主要内容,如果未能解决你的问题,请参考以下文章

    前端最强面试宝典 - JS 篇之数据类型

    前端笔记整理(JS)

    前端笔记整理(JS)

    前端笔记整理(JS)

    web前端工程师这些年被问烂了的面试题JavaScript

    js判断是否数组的方法