如何手动实现一个New操作
写在前面
在所有的前端面试中常常喜欢考面试者如何手写一个new操作符,作为在准备秋招的大三党,我也要考虑这些。
那么我们先看看new操作符都干了什么事情,有哪些操作?通过下面的代码来进行思考:
function Otaku(name, age) { this.name = name; this.age = age; this.habit = 'pk'; }
Otaku.prototype.strength = 60; Otaku.prototype.sayYourName = function () { console.log('I am ' + this.name); }
const person = new Otaku('乔峰',5000); person.sayYourName(); console.log(person); ``` ![控制台打印结果](http://p9utic4op.bkt.clouddn.com/new.png)
## 解析
从控制台打印出来的结果我们可以看出new操作符大概做了一下几件事情:
1. 返回(产生)了一个新的对象 2. 访问到了类Otaku构造函数里的属性 3. 访问到Otaku原型上的属性和方法 并且设置了this的指向(指向新生成的实例对象) 通过上面的分析展示,可以知道new团伙里面一定有Object的参与,不然对象的产生就有点说不清了。 先来边写写:
```js
const person = new Otaku('乔峰',5000); const person1 = objectFactory(Otaku, '鸠摩智', 5000);
function objectFactory(obj, name, age) {}
function objectFactory() { console.log(arguements); const Constructor = [].shift.call(arguments); const args = arguments; let obj = new Object(); Constructor.call(obj,...args); return obj; }
```
- 上面的代码注释太多,剔除注释以后的代码:
```js function objectFactory() { let Constructor = [].shift.call(arguments); const obj = new Object(); obj.__proto__ = Conctructor.prototype; Constructor.call(obj,...arguments); return obj; } ``` - 还有另外一种操作:
```js function myNew(Obj,...args){ var obj = Object.create(Obj.prototype); Obj.apply(obj,args); return obj; }
|
闭包
闭包是由该函数和其执行上下文共同构成,能够读取其他函数那边变量的函数。
可以用来做数据缓存、对象的私用方法等
数组
数组
定义:
一个存储元素的线性集合, 元素可以通过索引(通常为数字)来任意存取。
数字索引在内部被转换为字符串类型、这是因为在javaScript中对象的属性名必须是字符串。而数组只是一种特殊的对象
创建数组
- 通过
构造函数
<!-- 传入一组元素进行数组初始化 --> var arr = New Array(1, 2, 3, 4, 5); print(arr.length); <!-- 只传一个元素,声明数组的初始化长度, 其中每个元素初始化为 undefined --> var arr1 = new Array(10); print(arr1.length);
|
JS 基本数据类型和引用类型
Js 基本数据类型
js基本数据类型包括:undefined
, null
, number
, boolean
, string
, symbol
, bigInt(新增)
。基本数据类型是按值访问的,就是说我们可以操作保存在变量中的实际的值
1.基本数据类型的值是不可改变的
任何方法都无法改变一个基本类型的值是不可改变的,比如一个字符串:
var name = "change"; name.substr(); console.log(name);
var s = "hello"; s.toUpperCase() console.log(s)
|
通过这两个例子, 我们原来发现定义的变量 name 的值始终没有发生改变,而调用 substr() 和 toUpperCase() 方法后返回的是一个新的字符串,跟原先定义的变量 name 并没有关系
按值访问
按值进行访问,操作的是保存在变量中实际的值
不可添加方法属性
基础类型的比较是值的比较
基础类型存放在栈区 变量标识符 + 变量值
引用类型
同时保存在栈区和堆区中
栈区保存变量标识符和指向堆区的方法
基本包装类型(包装对象)
this
this-用于访问当前方法所属的对象
const Obj = { name: 'jack', fn() { console.log(this == Obj); } }
Obj.fn();
|
function showThis() { console.log(this); }
show();
'use strict'; function showThis() { console.log(this); }
showThis();
setTimeout(showThis, 100);
window.setTimeout(showThis, 100);
|
每个新生成的函数内部都会新建一个 this、这个 this 在函数被调用的时候被绑定
this 在运行时进行绑定
this 提供了一种更为优雅的方式隐式传递一个对象的引用,让 API 设计更加简洁且易于复用
应用场景:
- 普通函数中的 this 指向全局
- 构造器里的 this 指向 new 返回的新对象
- 函数作为方法被调用时,this 指向该对象
- 箭头函数不会创建自己的 this,使用一个封闭上下文中的 this
改变 this 的指向:
forEach 中的 this 指向
const myForEach(cb, thisArg) { for(let i = 0; i < this.length; i ++) { cb.call(thisArg, (this[i])); } }
const arr = [1, 2, 3]; arr.forEach(function(item){ console.log(this, item); })
|
改变 this 指向 call 里面的参数
JS对数组去重的几种方法
面试经常问的一道题
JS对数组去重的几种方法 (前面六种方法是普通数组,最后一种是对象数组)