FED1 事件委托
描述
请补全JavaScript代码,要求如下:
1. 给"ul"标签添加点击事件
2. 当点击某"li"标签时,该标签内容拼接"."符号。如:某"li"标签被点击时,该标签内容为".."
注意:
1. 必须使用DOM0级标准事件(onclick)
<body>
<ul>
<li>.</li>
<li>.</li>
<li>.</li>
</ul>
<!-- 填写标签 -->
<script type="text/javascript">
// 填写JavaScript
document.querySelector('ul').onclick = event => {
event.target.innerText+='.' //填写代码区域
}
</script>
</body>
FED2 数组去重
描述
请补全JavaScript代码,要求去除数组参数中的重复数字项并返回该数组。
注意:
1. 数组元素仅包含数字
示例1
输入:_deleteRepeat([-1,1,2,2])
输出:[-1,1,2]
<script type="text/javascript">
const _deleteRepeat = array => {
// 补全代码
for(let i=0;i<array.length-1;i++){
for(let j=1;j<array.length;j++){
if(array[i]==array[j]){
array.splice(j,1)
}
}
}
return array
}
</script>
FED3 合法的URL
描述
请补全JavaScript代码,要求以Boolean的形式返回字符串参数是否为合法的URL格式。
注意:
1. 协议仅为HTTP(S)
<script type="text/javascript">
const _isUrl = url => {
// 补全代码
// 开始符 ^
// 协议部分http(s):// 表示为((https|http|ftp|rtsp|mms)?:\/\/)
// 域名部分 表示为(([A-Za-z0-9]+-[A-Za-z0-9]+|[A-Za-z0-9]+)\.)+
// 顶级域名com cn等为2-6位 表示为([a-zA-Z]{2,6})
// 端口部分 表示为(:\d+)?, ?表示0次或1次
// 请求路径如/login 表示为 (\/.*)?
// 问号传参及哈希值如?age=1 表示为 (\?.*)?和(#.*)?
// 结束符 $
let reg = /^((https|http|ftp|rtsp|mms)?:\/\/)(([A-Za-z0-9]+-[A-Za-z0-9]+|[A-Za-z0-9]+)\.)+([A-Za-z]{2,6})(:\d+)?(\/.*)?(\?.*)?(#.*)?$/
return reg.test(url)
}
</script>
FED4 快速排序
描述
请补全JavaScript代码,要求将数组参数中的数字从小到大进行排序并返回该数组。
注意:
1. 数组元素仅包含数字
2. 请优先使用快速排序方法
示例1
输入:_quickSort([0,-1,1,-2,2])
输出:[-2,-1,0,1,2]
<!-- 填写标签 -->
<script type="text/javascript">
// 填写JavaScript
const _quickSort = array => {
if(array.length <= 1 ) return array;
const left = [];
const right = [];
let pivot = array[0];
for(let i = 1; i<array.length;i++) {
if(pivot > array[i]) {
left.push(array[i])
} else {
right.push(array[i])
}
}
return [..._quickSort(left),pivot,..._quickSort(right)]
}
</script>
FED5 全排列
描述
请补全JavaScript代码,要求以数组的形式返回字符串参数的所有排列组合。
注意:
1. 字符串参数中的字符无重复且仅包含小写字母
2. 返回的排列组合数组不区分顺序
示例1
输入:_permute('abc')
复制输出:['abc','acb','bac','bca','cab','cba']
const _permute = string => {
// 补全代码
if (string.length === 1) {
return [string];
}
let strArr = string.split('');
let result = []; // 存放每次生成的排列串
for (let i = 0, len = strArr.length; i < len; i++) {
restArr = strArr.filter(item => item !== strArr[i]);
let pailie = _permute(restArr.join(''));
result = result.concat(pailie.map(item => strArr[i] + item));
}
return result;
}
FED6 instanceof
描述
请补全JavaScript代码,要求以Boolean的形式返回第一个实例参数是否在第二个函数参数的原型链上。
/* 在原型链上查找
使用接口是Object.getPrototypeOf()
先去除基本数据类型的干扰
在获取第一个类型的原型
用第一个类型的原型和第二个类型的原型进行比较
:如果相当 则返回true
:如果不相等 则第一个原型再往上找一层 再比较; */
const _instanceof = (target, Fn) => {
// 补全代码
//基本数据类型的判断
if(target === null || typeof target !== 'object'){
return false
}
//获取第一个原型
let leftProto = Object.getPrototypeOf(target)
while(true){
if (leftProto === null){
return false
}
//和第二个原型进行比较
if(leftProto == Fn.prototype){
return true
}
leftProto = Object.getPrototypeOf(leftProto)
}
}
FED7 Array.map
描述
请补全JavaScript代码,要求实现Array.map函数的功能且该新函数命名为"_map"。
示例1
输入:[1,2]._map(i => i * 2)
输出:[2,4]
<!-- 填写标签 -->
<script type="text/javascript">
//
Array.prototype._map = function(fun){
//补全代码
if(this.length!==0){
for(let i = 0;i<this.length;i++){
this[i] = fun(this[i]);
}
}
return this;
}
</script>
FED8 Array.filter
描述
请补全JavaScript代码,要求实现Array.filter函数的功能且该新函数命名为"_filter"。
示例1
输入:[1,2]._filter(i => i>1)
输出:[2]
<!-- 填写标签 -->
<script type="text/javascript">
// 填写JavaScript
Array.prototype._filter=function(fn){
let arr=this
let newArr=[]
for(let i=0;i<arr.length;i++){
if(fn(arr[i])) newArr.push(arr[i])
}
return newArr
}
</script>
FED9 Array.reduce
描述
请补全JavaScript代码,要求实现Array.reduce函数的功能且该新函数命名为"_reduce"。
示例1
输入:[1,2,3]._reduce((left, right) => left + right)
输出:6
/* reduce接受两个参数,一个是回调函数,另一个就是初始值。而回调函数又有四个参数,分别是:
accumulator 累加器
currentValue 当前值
index 当前索引
array 调用reduce的数组
这里要注意的是,当传入初始值的时候,reduce中的index 是从0开始,而没有传入初始值的时候,index是从1开始。所以控制好累加器和当前index就可以了。 */
const reduce = function (cb, init) {
if (typeof cb != 'function') {
throw new Error(`${cb} is not a function`);
}
// 累加器
let accumulator = init || this[0];
let currentIndex = init ? 0 : 1;
for (currentIndex; currentIndex < this.length; currentIndex++) {
accumulator = cb(accumulator, this[currentIndex], currentIndex, this);
}
return accumulator;
}
Array.prototype._reduce = reduce;
FED10 _objectCreate
描述
请补全JavaScript代码,要求实现Object.create函数的功能且该新函数命名为"_objectCreate"。
本题考点:原型对象
根据题目要求,实现一个仿Object.create功能的"_objectCreate"函数,该函数创建一个新对象,使用现有的对象来提供新创建的对象的proto,核心步骤有:
- 创建一个临时函数
- 将该临时函数的原型指向对象参数
- 返回该临时对象的实例
参考答案:
Object.create法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
const _objectCreate = proto => {
if(typeof proto !== 'object' || proto === null) return
const fn = function() {}
fn.prototype = proto
return new fn()
}
FED11 _call函数
描述
请补全JavaScript代码,要求实现Function.call函数的功能且该新函数命名为"_call"。
<!-- 填写标签 -->
<script type="text/javascript">
// 填写JavaScript
Function.prototype._call = function (context, ...args) {
// 判断context,如果为null或者undefined,直接指向window
let cxt = context || window;
// 新建一个唯一的Symbol,避免重复
let func = Symbol();
cxt[func] = this;
args = args ? args : [];
// 以对象的方式调用func,此时的this为传入需要绑定的this指向
const res = args.length > 0 ? cxt[func](args) : cxt[func]();
// 删除方法,避免对全局造成污染
delete cxt[func];
return res;
};
</script>
FED12 Function.bind
描述
请补全JavaScript代码,要求实现Function.bind函数的功能且该新函数命名为"_bind"。
<script type="text/javascript">
// 补全代码
Function.prototype._bind = function(_this, ...args1) {
return () => {
return this.apply(_this, ...args1);
}
}
</script>
FED13 实现new操作符
描述
请补全JavaScript代码,要求实现new操作符的功能。
根据题目要求,实现一个仿new功能的新"_new"函数,该函数会返回一个对象,该对象的构造函数为函数参数、原型对象为函数参数的原型,核心步骤有:
- 创建一个新对象
- 获取函数参数
- 将新对象的原型对象和函数参数的原型连接起来
- 将新对象和参数传给构造器执行
- 如果构造器返回的不是对象,那么就返回第一个新对象
<script type="text/javascript">
const _new = function() {
// 补全代码
const object1 = {}
const Fn = [...arguments].shift()
object1.__proto__ = Fn.prototype
const object2 = Fn.apply(object1, arguments)
return object2 instanceof Object ? object2 : object1
}
</script>
FED14 Object.freeze
描述
请补全JavaScript代码,要求实现Object.freeze函数的功能且该新函数命名为"_objectFreeze"
<script type="text/javascript">
const _objectFreeze = object => {
// 补全代码
let props = Object.getOwnPropertyNames(object)
for (let prop of props) {
const des = Object.getOwnPropertyDescriptor(object, prop)
if (des.get || des.set) {
Object.defineProperty(object, prop, {
configurable: false,
get: des.get,
set: des.set
})
} else {
Object.defineProperty(object, prop, {
writable: false,
configurable: false
})
}
}
return Object.preventExtensions(object)
// return Object.seal(object)
}
</script>
/* freeze = 不可扩展(preventExtensions) + 不可配置(configurable) + 不可写(writable)
seal = 不可扩展 + 不可配置
preventExtansions = 不可添加属性 + 不可改__proto__
还有要访问器属性的get和set */
FED15 浅拷贝
描述
请补全JavaScript代码,要求实现一个对象参数的浅拷贝并返回拷贝之后的新对象。
注意:
1. 参数可能包含函数、正则、日期、ES6新对象
浅拷贝核心点在于:
- 对于基础数据类型数据直接拷贝
- 对于引用类型数据仅拷贝第一层对象的属性,重新开辟一个地址将其存储(深拷贝和浅拷贝的重要差异)
<script type="text/javascript">
const _shallowClone = target => {
// 补全代码
// 基本数据类型
if(typeof target !== 'object' || target === null) return target;
let ret = Array.isArray(target)?[]:{};
// Object.assign(ret, target);
let str = Object.prototype.toString.call(target);
// 函数,日期正则,直接返回
if(str == '[object Date]' || str == '[object RegExp]' || str == '[object Function]')
return target;
Object.keys(target).forEach(key=>{
ret[key] = target[key];
})
return ret;
}
</script>
FED16 简易深拷贝
描述
请补全JavaScript代码,要求实现对象参数的深拷贝并返回拷贝之后的新对象。
注意:
1. 参数对象和参数对象的每个数据项的数据类型范围仅在数组、普通对象({})、基本数据类型中]
2. 无需考虑循环引用问题
根据题目要求,实现对象参数的深拷贝并返回拷贝之后的新对象,因为参数对象和参数对象的每个数据项的数据类型范围仅在数组、普通对象({})、基本数据类型中且无需考虑循环引用问题,所以不需要做过多的数据类型判断,核心步骤有:
- 如果对象参数的数据类型不为“object”或为“null”,则直接返回该参数
- 根据该参数的数据类型是否为数组创建新对象
- 遍历该对象参数,将每一项递归调用该函数本身的返回值赋给新对象
const _sampleDeepClone = target => {
if(typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? [] : {}
for(prop in target) {
if(target.hasOwnProperty(prop)) {
cloneTarget[prop] = _sampleDeepClone(target[prop])
}
}
return cloneTarget
} else {
return target
}
}
FED17 深拷贝
描述
请补全JavaScript代码,要求实现对象参数的深拷贝并返回拷贝之后的新对象。
注意:
1. 需要考虑函数、正则、日期、ES6新对象
2. 需要考虑循环引用问题
根据题目要求,实现对象参数的深拷贝并返回拷贝之后的新对象,因为需要考虑参数对象和参数对象的每个数据项的数据类型可能包括函数、正则、日期、ES6新对象且必须考虑循环引用问题,所以需要引入ES6新对象Map并且详细的判断数据类型,核心步骤有:
- 首先判断对象参数是否为“null”,是则返回“null”
- 判断对象参数数据类型是否为“object”,不是则返回该参数
- 获取到对象参数的构造函数名,判断是否为函数、正则、日期、ES6新对象其中之一,如果是则直接返回通过该参数对象对应的构造函数生成的新实例对象
- 当以上条件判断之后函数依然没有结束时继续进行以下操作
- 在Map对象中获取当前参数对象,如果能获取到,则说明这里为循环引用并返回Map对象中该参数对象的值
- 如果在Map对象中没有获取到对应的值,则保存该参数对象到Map中,作为标记
- 根据该参数的数据类型是否为数组创建新对象
- 遍历该对象参数,将每一项递归调用该函数本身的返回值赋给新对象
const _completeDeepClone = (target, map = new Map()) => {
if(target === null) return target
if(typeof target !== 'object') return target
const constructor = target.constructor
if(/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target)
if(map.get(target)) return map.get(target)
map.set(target, true)
const cloneTarget = Array.isArray(target) ? [] : {}
for(prop in target) {
if(target.hasOwnProperty(prop)) {
cloneTarget[prop] = _completeDeepClone(target[prop], map)
}
}
return cloneTarget
}
FED18 寄生组合式继承
描述
请补全JavaScript代码,要求通过寄生组合式继承使"Chinese"构造函数继承于"Human"构造函数。要求如下:
1. 给"Human"构造函数的原型上添加"getName"函数,该函数返回调用该函数对象的"name"属性
2. 给"Chinese"构造函数的原型上添加"getAge"函数,该函数返回调用该函数对象的"age"属性
根据题目要求,通过寄生组合式继承使"Chinese"构造函数继承于"Human"构造函数。寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的形式来继承方法,只调用了一次父类构造函数,效率高,也避免了在子类的原型对象上创建不必要的、多余的属性,原型链也不会被改变,核心步骤有:
- 在"Human"构造函数的原型上添加"getName"函数
- 在”Chinese“构造函数中通过call函数借助”Human“的构造器来获得通用属性
- Object.create函数返回一个对象,该对象的__proto__属性为对象参数的原型。此时将”Chinese“构造函数的原型和通过Object.create返回的实例对象联系起来
- 最后修复"Chinese"构造函数的原型链,即自身的"constructor"属性需要指向自身
- 在”Chinese“构造函数的原型上添加”getAge“函数
function Human(name) {
this.name = name
this.kingdom = 'animal'
this.color = ['yellow', 'white', 'brown', 'black']
}
Human.prototype.getName = function() {
return this.name
}
function Chinese(name,age) {
Human.call(this,name)
this.age = age
this.color = 'yellow'
}
Chinese.prototype = Object.create(Human.prototype)
Chinese.prototype.constructor = Chinese
Chinese.prototype.getAge = function() {
return this.age
}
FED19 发布订阅模式
描述
请补全JavaScript代码,完成"EventEmitter"类实现发布订阅模式。
注意:
1. 同一名称事件可能有多个不同的执行函数
2. 通过"on"函数添加事件
3. 通过"emit"函数触发事件
根据题目要求,完成"EventEmitter"类实现发布订阅模式,考虑到同一名称事件可能有多个不同的执行函数,所以在构造函数中需要以对象的结构存放事件,核心步骤有:
- 构造函数中创建”events“对象变量用于存放所有的事件
- 添加”on“函数,用于订阅事件。当总事件中不存在此事件时创建新的事件数组,当存在时将”fn“函数添加在该事件对应数组中
- 添加”emit“函数,用于发布事件,遍历该事件下的函数数组并全部执行
class EventEmitter {
constructor() {
this.events = {}
}
on(event, fn) {
if(!this.events[event]) {
this.events[event] = [fn]
} else {
this.events[event].push(fn)
}
}
emit(event) {
if(this.events[event]) {
this.events[event].forEach(callback => callback())
}
}
}
FED20 观察者模式
描述
请补全JavaScript代码,完成"Observer"、"Observerd"类实现观察者模式。要求如下:
1. 被观察者构造函数需要包含"name"属性和"state"属性且"state"初始值为"走路"
2. 被观察者创建"setObserver"函数用于保存观察者们
3. 被观察者创建"setState"函数用于设置该观察者"state"并且通知所有观察者
4. 观察者创建"update"函数用于被观察者进行消息通知,该函数需要打印(console.log)数据,数据格式为:小明正在走路。其中"小明"为被观察者的"name"属性,"走路"为被观察者的"state"属性
注意:
1. "Observer"为观察者,"Observerd"为被观察者
根据题目要求完成"Observer"、"Observerd"类实现观察者模式。核心步骤有:
- 被观察者构造函数声明三个属性分别为"name"用于保存被观察者姓名、"state"用于保存被观察者状态、"observers"用于保存观察者们
- 被观察者创建"setObserver"函数,该函数通过数组的push函数将观察者参数传入"observers"数组中
- 被观察者创建"setState"函数,该函数首先通过参数修改被观察者的"state"属性,然后通过遍历"observers"数组分别调用各个观察者的"update"函数并且将该被观察者作为参数传入
- 观察者创建"update"函数,用于打印信息
class Observerd {
constructor(name) {
this.name = name
this.state = '走路'
this.observers = []
}
setObserver(observer) {
this.observers.push(observer)
}
setState(state) {
this.state = state
this.observers.forEach(observer => observer.update(this))
}
}
class Observer {
constructor() {
}
update(observerd) {
console.log(observerd.name + '正在' + observerd.state)
}
}