前言
万丈高楼平地起,一砖一瓦届地基。我觉得要来一波死磕 36 个 JS 手写题
三道js手写题
数据类型的判断
基础知识:
-
js的数据类型分为基础类型和引用类型。 基础类型: Number, Null, Undefind, String,BigInt, Boolean, Symbol。 引用类型: Date, RegExp, Array, Function, Math。
-
2. js中自带的判断数据类型 typeof: typeof 可以识别基础数据类型 + function (但Null除外) (typeof null) 返回为 "object" 虽然 typeof null 会输出 object,但这只是 JS 存在的一个悠久 Bug, 不代表 null 就是引用数据类型,并且 null 本身也不是对象。 instanceof: 我们 new 一个对象,那么这个新对象就是它原型链继承上面的对象了, 通过 instanceof 我们能判断这个对象是否是之前那个构造函数生成的对象, 这样就基本可以判断出这个新对象的数据类型。 Object.prototype.toString: toString() 是 Object 的原型方法,调用该方法, 可以统一返回格式为 “[object Xxx]” 的字符串, 其中 Xxx 就是对象的类型。对于 Object 对象, 直接调用 toString() 就能返回 [object Object]; 而对于其他对象,则需要通过 call 来调用,才能返回正确的类型信息。
手写判断数据类型(返回 boolean 或 function)
function myTypeof(obj) {
let datatype = Object.prototype.toString.call(obj)
// 或者
// datatype = datatype.slice(8, -1).toLowerCase();
// 这句话的意思是让我们用Object原型上的toString方法
// 作用在传入的obj的上下文中(通过call将this指向obj)
datatype = datatype.split(' ')[1]
// 用split切成数组, 并取数组的第两项
datatype = datatype.splice(0, datatype.length-1).toLowerCase()
return datatype;
}
js中的继承方法
第一种原型链继承
function Parent1() {
this.name = 'parent1';
this.play = [1, 2, 3]
}
function Child1() {
this.type = 'child2';
}
Child1.prototype = new Parent1();
第二种构造函数继承
function Animal(name) {
this.name = name;
this.getName = function() {
return this.name;
}
}
function Dog(name) {
Animal.call(this, name);
}
let a = new Dog('人类');
console.log(a.getName());
但是由于方法必须定义在构造函数中,所以会导致每次创建子类实例都会创建一遍方法。
第三种组合式继承
function Animal(name) {
this.name = name;
this.colors = ["白色", "黑色"];
}
Animal.prototype.getName = function() {
return this.name;
}
function Dog(name, age) {
Animal.call(this, name);
this.age = age;
}
// Dog.prototype = new Animal()
// 副作用 会把Dog.prototype.constructor Cat 变成Animal
// Dog.prototype.constructor = Dog;
// 或者直接
Dog.prototype.__proto__ == Animal.prototype;
console.log(Dog.prototype.constructor,'oooooooo');
let dog1 = new Dog('奶昔', 2)
dog1.colors.push('粉色')
let dog2 = new Dog('阿黄', 1);
dog2.colors.push('银白色');
console.log(dog2)
第四种寄生式继承
function clone (parent, child) {
// 寄生式继承使用了Object.create()
// 这个方法接收两个参数:一是用作新对象原型的对象
// 二是为新对象定义额外属性的对象(可选参数)
// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype);
// child.prototype.__proto__ -> parent.prototype
child.prototype.constructor = child;
}
function Parent6() {
this.name = 'parent6';
this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () {
return this.name;
}
function Child6() {
Parent6.call(this);
this.friends = 'child5';
}
clone(Parent6, Child6);
Child6.prototype.getFriends = function () {
return this.friends;
}
let person6 = new Child6();
console.log(person6);
console.log(person6.getName());
console.log(person6.getFriends());
第五种extends继承
class Animal {
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class Dog extends Animal {
constructor(name, age) {
super(name)
// super指向的是当前对象的原型对象, 即为Animal
this.age = age
}
}
数组去重
第一种借用filter
function unique(arr) {
// filter() 方法创建一个新的数组,
// 新数组中的元素是通过检查指定数组中符合条件的所有元素
var res = arr.filter(function(item, index, array) {
// console.log(array, 'array');
return array.indexOf(item) === index
})
// item 必须。当前元素的值
// index 可选。当前元素的索引值
// array 可选。当前元素属于的数组对象
return res
}
第二种借用reduce
function unique1(arr) {
let un = arr.reduce((pre,cur) =>{
if(!pre.includes(cur)) {
pre.push(cur)
}
return pre
},[])
return un
}
第三种Set结构
function unique2(arr) {
return [...new Set(arr)]
// Set也是一个构造函数,其数据结构类似于数组,
// 但其不同之处在于永远没有重复的值 。
// add(value):添加某个值,返回Set结构本身。
// delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
// has(value):返回一个布尔值,表示该值是否为Set的成员。
// clear():清除所有成员,没有返回值。
}
第四种Map结构
function unique3(arr) {
let map = new Map();
console.log(map)
//let arr1 = new Array(); // 数组用于返回结果
let arr1 = []
for (let i = 0, len = arr.length; i < len; i++) {
if (map.has(arr[i])) { // 判断是否存在该key值
map.set(arr[i], true);
}
else {
map.set(arr[i], false);
arr1.push(arr[i]);
}
console.log(map)
}
return arr1;
}
后话
对象这种js手写题可以先看题干,再着手进行写出相关代码。