10个常考的前端手写题,你全都会吗?(上)

前言

 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!

 🍅 个人主页:南木元元

今天来分享一下10个常见的JavaScript手写功能。


目录

1.实现new

2.call、apply、bind

实现call

实现apply

实现bind

3.防抖和节流

防抖

节流

4.实现instanceof

5.实现Ajax

6.深拷贝和浅拷贝

浅拷贝

深拷贝

7.函数柯里化

参数定长的柯里化

参数不定长的柯里化

8.数组扁平化

9.数组去重

10.手写类型判断函数

结语


1.实现new

(1)首先创建一个新的空对象。

(2)设置原型,将对象的原型设置为函数的prototype对象。

(3)让函数的this指向这个对象,执行构造函数的代码。

(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

function myNew(constructor, ...args) {
  // 如果不是一个函数,就报错
  if (typeof constructor !== "function") {
    throw "myNew function the first param must be a function";
  }
  // 基于原型链 创建一个新对象,继承构造函数constructor的原型对象上的属性
  let newObj = Object.create(constructor.prototype);
  // 将newObj作为this,执行 constructor ,传入参数
  let res = constructor.apply(newObj, args);
  // 判断函数的执行结果是否是对象,typeof null 也是'object'所以要排除null
  let isObject = typeof res === "newObject" && res !== null;
  // 判断函数的执行结果是否是函数
  let isFunction = typeof res === "function";
  // 如果函数的执行结果是一个对象或函数, 则返回执行的结果, 否则, 返回新创建的对象
  return isObject || isFunction ? res : newObj;
}

// 用法
function Person(name, age) {
  this.name = name;
  this.age = age;
 // 如果构造函数内部,return 一个引用类型的对象,则整个构造函数失效,而是返回这个引用类型的对象
}
Person.prototype.say = function() {
  console.log(this.age);
};
let p1 = myNew(Person, "poety", 18);
console.log(p1.name);	//poety
console.log(p1);	//Person {name: 'poety', age: 18}
p1.say();	//18

测试结果: 

2.call、apply、bind

实现call

思路:接受传入的context上下文,如果不传默认为window,将被调用的方法设置为上下文的属性,使用上下文对象来调用这个方法,删除新增属性,返回结果。

//写在函数的原型上
Function.prototype.myCall = function (context) {
  // 如果要调用的方法不是一个函数,则报错
  if (typeof this !== "function") {
    throw new Error("Type error");
  }
  // 判断 context 是否传入,如果没有传就设置为 window
  context = context || window;
  // 获取参数,[...arguments]把类数组转为数组
  let args = [...arguments].slice(1);
  let result = null;
  // 将被调用的方法设置为context的属性,this即为要调用的方法
  context.fn = this;
  // 执行要被调用的方法
  result = context.fn(...args);
  // 删除手动增加的属性方法
  delete context.fn;
  // 将执行结果返回
  return result;
};

//测试
function f(a,b){
    console.log(a+b)
    console.log(this.name)
}
let obj={
    name:1
}
f.myCall(obj,1,2) // 3,1

测试结果:

实现apply

除了传参方式是数组,其它与call没区别。

Function.prototype.myApply = function (context) {
  if (typeof this !== "function") {
    throw new Error("Type error");
  }
  let result = null;
  context = context || window;
  // 与上面代码相比,我们使用 Symbol 来保证属性唯一,也就是保证不会重写用户自己原来定义在 context 中的同名属性
  const fnSymbol = Symbol();
  context[fnSymbol] = this;
  // 执行要被调用的方法,处理参数和 call 有区别,判断是否传了参数数组
  if (arguments[1]) {//传了参数数组
    result = context[fnSymbol](...arguments[1]);
  } else {//没传参数数组
    result = context[fnSymbol]();
  }
  delete context[fnSymbol];
  return result;
};

//测试
function f(a,b){
  console.log(a,b)
  console.log(this.name)
}
let obj={
  name:'张三'
}
f.myApply(obj,[1,2])	//1 2,张三

测试结果:

实现bind

bind返回的是一个函数,需要判断函数作为构造函数的情况,当作为构造函数时,this 指向实例,不会被任何方式改变 this,所以要忽略传入的context上下文。

bind可以分开传递参数,所以需要将参数拼接。

如果绑定的是构造函数,还需要继承构造函数原型上的属性和方法,保证不丢失。

Function.prototype.myBind = function (context) {
  // 判断调用对象是否为函数
  if (typeof this !== "function") {
    throw new Error("Type error");
  }
  // 获取参数
  const args = [...arguments].slice(1);
  const fn = this; // 保存this的值,代表调用bind的函数
  //返回一个函数,此函数可以被作为构造函数调用,也可以作为普通函数调用
  const Fn = function () {
    // 根据调用方式,传入不同绑定值
    // 当作为构造函数时,this 指向实例,不会被任何方式改变 this,要忽略传入的context上下文
    return fn.apply(
      this instanceof Fn ? this : context,
      // bind可以分开传递参数(如f.bind(obj, 1)(2)),所以需要将参数拼接,这里使用apply,参数拼接成一个数组
      args.concat(...arguments)//当前的这个 arguments 是指 Fn 的参数,也可以用剩余参数的方式
    );
  };
  //对于构造函数,要保证原函数的原型对象上的属性不能丢失
  Fn.prototype = Object.create(fn.prototype);
  return Fn;
};

// 1.先测试作为构造函数调用
function Person(name, age) {
  console.log(name);
  console.log(age);
  console.log(this); //构造函数this指向实例对象
}
// 构造函数原型的方法
Person.prototype.say = function () {
  console.log("say");
};
var obj = {
  name: "cc",
  age: 18,
};
var bindFun = Person.myBind(obj, "cxx");
var a = new bindFun(10);
// cxx
// 10
// Person {}
a.say(); // say

// 2.再测试作为普通函数调用
function normalFun(name, age) {
  console.log(name);
  console.log(age);
  console.log(this); // 普通函数this指向绑定bind的第一个参数 也就是例子中的obj
}
var obj = {
  name: "aa",
  age: 18,
};
var bindNormalFun = normalFun.myBind(obj, "cc");
bindNormalFun(12);
// cc
// 12
// { name: 'aa', age: 18 }

测试结果: 

3.防抖和节流

防抖

防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

//fn是需要防抖的函数,delay是等待时间
function debounce(fn, delay = 500) {
    let timer = null;
    // 这里返回的函数是每次用户实际调用的防抖函数
    return function(...args) {	//...args是es6的剩余参数语法,将多余的参数放入数组,用来代替arguments对象
    	// 如果已经设定过定时器了就清空上一次的定时器
    	if(timer) {
        	clearTimeout(timer);	
        }
        // 开始一个新的定时器,延迟执行用户传入的方法;注:定时器的返回值是一个数值,作为定时器的编号,可以传入clearTimeout来取消定时器
        timer = setTimeout(() => {  //这里必须是箭头函数,不然this指向window,要让this就指向fn的调用者
        	fn.apply(this, args);   
        }, delay)	
    }
}

节流

节流就是一定时间内只执行一次事件,即使重复触发,也只有一次生效。可以使用在监听滚动scroll事件上,通过事件节流来降低事件调用的频率。

  • 定时器版本
function throttle(fn, delay = 500) {
    let timer = null;
    return function(...args) {
    	// 当前有任务了,直接返回
        if(timer) {
        	return;
        }
        timer = setTimeout(() => {
            fn.apply(this, args);
            //执行完后,需重置定时器,不然timer一直有值,无法开启下一个定时器
            timer = null;	
        }, delay)
    }
}
  • 时间戳版本
// 节流
function throttle(fn, delay = 500) {
  let prev = Date.now();// 上一次执行该函数的时间
  return function(...args) {
    let now = Date.now();//返回从UTC到当前时间的毫秒数
    // 如果差值大于等于设置的等待时间就执行函数
    if (now - prev >= delay) {
      fn.apply(this, args);
      prev = Date.now();
    }
  };
}

详细可以去看我的这篇文章——前端性能优化之防抖&节流

4.实现instanceof

instanceof用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

function myInstanceof(instance, constructor) {
    //如果不是对象,或者是null,直接返回false
    if (typeof instance !== "object" || instance === null) {
    	return false;
    }
    // 获取对象的原型
    let proto = Object.getPrototypeOf(instance);
    // 获取构造函数的 prototype 对象
    let prototype = constructor.prototype;
    // 判断构造函数的 prototype对象是否在对象的原型链上
    while (true) {
        // 到达原型链终点null,说明没找到
        if (!proto) {
        	return false;
        }
        if (proto === prototype) {
        	return true;
        }
        // 如果没有找到,就继续从其原型上找
        proto = Object.getPrototypeOf(proto);
    }
}

//测试
let Fn = function () { }
let p1 = new Fn()
console.log(myInstanceof(p1, Fn));//true
console.log(myInstanceof([], Fn));//false
console.log(myInstanceof([], Array)) // true
console.log(myInstanceof(function(){}, Function)) // true

测试结果: 

5.实现Ajax

(1)创建一个 XMLHttpRequest 对象。

(2)在这个对象上使用 open 方法创建一个 HTTP 请求(参数为请求方法、请求地址、是否异步和用户的认证信息)。

(3)通过send方法来向服务器发起请求(post请求可以入参数作为发送的数据体)。

(4)监听请求成功后的状态变化:根据状态码进行相应的处理。onreadystatechange设置监听函数,当对象的readyState变为4的时候,代表服务器返回的数据接收完成,这个时候可以通过判断请求的状态,如果状态是200则代表成功,404或500代表失败。

function ajax(url) {
    //1.创建XMLHttpRequest对象
    const xhr = new XMLHttpRequest();
    //2.使用open方法创建一个GET请求
    xhr.open('GET',url);
    //xhr.open('GET',url,true);//true代表异步,已完成事务的通知可供事件监听器使用;如果为false,send() 方法直到收到答复前不会返回
    //3.发送请求
    xhr.send(); 
    //4.监听请求成功后的状态变化(readyState改变时触发):根据状态码(0~5)进行相应的处理
    xhr.onreadystatechange = function () {
        //readyState为4代表服务器返回的数据接收完成
        if (xhr.readyState == 4) { 
            //请求的状态为200或304代表成功
            if(xhr.status == 200 || xhr.status == 304) {
                 //this.response代表返回的数据
                handle(this.response);
            } else {
                //this.statusText代表返回的文本信息
                console.error(this.statusText);
            }
        }
    };
}

使用Promise封装AJAX:

function ajax(url) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open('get', url)
        xhr.send()  
        xhr.onreadystatechange = () => {
          if (xhr.readyState == 4) {
            if (xhr.status == 200 || xhr.status == 304) {
              resolve(this.response)
            } else {
              reject(new Error(this.statusText));
            }
          }
        }
  	})
}
//使用
let url = '/data.json'
ajax(url).then(res => console.log(res))
  .catch(reason => console.log(reason))

6.深拷贝和浅拷贝

浅拷贝

浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

//实现浅拷贝
function shallowCopy (obj){
    // 只拷贝对象,基本类型或null直接返回
    if(typeof obj !== 'object' || obj === null) {
    	return obj;
    }
    // 判断是新建一个数组还是对象
    let newObj = Array.isArray(obj) ? []: {};
    //for…in会遍历对象的整个原型链,如果只考虑对象本身的属性,需要搭配hasOwnProperty
    for(let key in obj ){
        //hasOwnProperty判断是否是对象自身属性,会忽略从原型链上继承的属性
        if(obj.hasOwnProperty(key)){
        	newObj[key] = obj[key];//只拷贝对象本身的属性
        }
    }
    return newObj;
}

//测试
var obj ={
    name:'张三',
    age:8,
    pal:['王五','王六','王七']
}
let obj2 = shallowCopy(obj);
obj2.name = '李四'
obj2.pal[0] = '王麻子'
console.log(obj); //{age: 8, name: "张三", pal: ['王麻子', '王六', '王七']}
console.log(obj2); //{age: 8, name: "李四", pal: ['王麻子', '王六', '王七']}

测试结果: 

深拷贝

深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

function deepCopy (obj, map = new WeakMap()){
    // 基本类型或null直接返回
    if(typeof obj !== 'object' || obj === null) {
    	return obj;
    }
    // 判断是新建一个数组还是对象
    let newObj = Array.isArray(obj) ? []: {};
    //利用map解决循环引用
    if (map.has(obj)) {
        return map.get(obj);
    }
    map.set(obj, newObj);//将当前对象作为key,克隆对象作为value
    for(let key in obj ){	
        if(obj.hasOwnProperty(key)){
        	newObj[key] = deepCopy(obj[key], map);	//递归
        }
    }
    return newObj
}

// 测试
let obj1 = {
    name : '南木元元',
    arr : [1,[2,3],4],
};
let obj2=deepCopy(obj1)
obj2.name = "阿元";
obj2.arr[1] = [5,6,7] ; // 新对象跟原对象不共享内存

console.log('obj1',obj1) // obj1 { name: '南木元元', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj2',obj2) // obj2 { name: '阿元', arr: [ 1, [ 5, 6, 7 ], 4 ] }

测试结果:

7.函数柯里化

函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

作用:可以参数复用(公共的参数已经通过柯里化预置了)和延迟执行(柯里化时只是返回一个预置参数的新函数,并没有立刻执行,在满足条件后才会执行)。

参数定长的柯里化

思路:通过函数的length属性获取函数的形参个数​​​​​,形参的个数就是所需参数的个数。维护一个数组,当数组的长度与函数接收参数的个数一致,再执行该函数。

// 实现函数柯里化
function curry(fn) {
  // 返回一个新函数
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args); // 如果参数够了,就执行原函数,返回结果
    } else {
      //返回一个新函数,继续递归去进行柯里化,利用闭包,将当前已经传入的参数保存下来
      return function (...args2) {
        //递归调用 curried 函数
        return curried.apply(this, [...args, ...args2]); //新函数调用时会继续传参,拼接参数
      };
    }
  };
}

// 测试
function sum(a, b, c) {
  return a + b + c;
}
var curried = curry(sum);
console.log(curried(1, 2, 3)); //6
console.log(curried(1, 2)(3)); //6
console.log(curried(1)(2, 3)); //6
console.log(curried(1)(2)(3)); //6

测试结果:

参数不定长的柯里化

题目:如何实现一个方法,使计算结果能够满足如下预期。

add(1, 2, 3) // 6
add(1) // 1
add(1)(2) // 3
add(1, 2)(3) // 6
add(1)(2)(3) // 6
add(1)(2)(3)(4) // 10

思路:利用闭包和递归,如果参数为空,则判断递归结束,求和,返回结果。

function addCurry() {
    // 利用闭包的特性收集所有参数值
    let arr = [...arguments]	
    //返回函数
    return function fn() {
        // 如果参数为空,则判断递归结束,即传入一个()执行函数
        if(arguments.length === 0) {	
	    	return arr.reduce((a, b) => a + b)	// 求和
        } else {
            arr.push(...arguments)
            return fn	//递归
        }
    }
}

// 测试
console.log(addCurry(1)());	//1
console.log(addCurry(1)(2)());	//3
console.log(addCurry(1)(2)(3)());	//6
console.log(addCurry(1, 2)(3)());	//6
console.log(addCurry(1, 2, 3)());	//6

上述写法,总是要以空括号()结尾,于是再改进为隐式转换.toString写法,原理:当用 Function的值做计算的时候,会调用toString做隐式转换。注意一些旧版本的浏览器隐式转换会默认执行,新版本不行了。可以利用隐式转换或者alert。

function addCurry() {
    let arr = [...arguments]
    // 利用闭包的特性收集所有参数值
    var fn = function() {
        arr.push(...arguments);
        return fn;//递归
    };
    // 利用 toString 隐式转换,转换的时候再返回结果
    fn.toString = function () {
        return arr.reduce(function (a, b) {
            return a + b;
        });
    }
    return fn;
}

//测试
console.log(addCurry(1)(2) == 3) //true 利用隐式转换,自动调用toString方法得到柯里化的结果
//alert(addCurry(1)(2)(3))//6 alert参数只能是字符串,如果其他类型的值,会转换成字符串,会调用toString方法
console.log(addCurry(1).toString());//1 手动调用toString
console.log(addCurry(1, 2)(3).toString());//6 
console.log(addCurry(1, 2)(3)(4)(5).toString());//15 

测试结果:

8.数组扁平化

数组扁平化其实就是将多维数组转为一维数组。

(1)ES6中的flat

const arr = [1,[2,[3,[4,5]]],6]
//  arr.flat([depth]) flat的参数代表的是需要展开几层,如果是Infinity的话,就是不管嵌套几层,全部都展开
console.log(arr.flat(Infinity)) //[1,2,3,4,5,6]

(2)递归

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    //如果当前元素还是一个数组
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));//递归拼接
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}
console.log(flatten(arr)); //  [1, 2, 3, 4]

(3)reduce函数迭代

从上面普通的递归函数中可以看出,其实就是对数组的每一项进行处理,那么其实也可以用reduce来实现数组的拼接,从而简化第一种方法的代码。

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
  return arr.reduce((total, cur) => {
    return total.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, []); //传递初始值空数组[],就会从数组索引为 0 的元素开始执行
}
console.log(flatten(arr)); //  [1, 2, 3, 4]

(4)split和toString

数组的toString方法可以把数组直接转换成逗号分隔的字符串。如[1, [2, [3, 4]]] => "1,2,3,4"

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
  //先把数组直接转换成逗号分隔的字符串,然后再用 split 方法把字符串重新转换为数组
  return arr.toString().split(",").map(Number); 
}
console.log(flatten(arr)); //  [ 1, 2, 3, 4 ]

9.数组去重

(1)利用Set。new一个Set,参数为需要去重的数组,Set 会自动删除重复的元素,再Array.from将 Set 转为数组返回。

const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log([...new Set(arr)]); //[ 1, 2, 3, 5, 9, 8 ]
console.log(Array.from(new Set(arr))); //[ 1, 2, 3, 5, 9, 8 ]

(2)利用数组的filter() + indexOf去重。利用filter方法,返回arr.indexOf(num)等于index的值。原理就是indexOf会返回最先找到的数字的索引。

function unique(arr) {
  return arr.filter((item, index, array) => {
    return array.indexOf(item) === index;
  });
}
const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log(unique(arr)); // [1, 2, 3, 5, 9, 8]

(3)利用map。新建一个数组和map,如果当前值在map中没有出现过,就加入数组,最后返回数组。

const unique = (arr) => {
    const map = new Map();
    const res = [];
    for (let item of arr) {
        if (!map.has(item)) {
            map.set(item, true);
            res.push(item);
        }
    }
    return res;
}
const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log(unique(arr)); // [1, 2, 3, 5, 9, 8]

10.手写类型判断函数

思路:如果是null,直接返回String(null);基本类型和函数,直接使用typeof其它引用类型,使用Object.prototype.toString.call。

function getType(value) {
  // 判断数据是 null 的情况
  if (value === null) {
    return String(value);
  }
  // 判断数据是基本数据类型的情况和函数的情况,使用typeof
  if (typeof value !== "object") {
    return typeof value;
  } else {
    // 判断数据是引用类型的情况,设当前类型为date
    let valueClass = Object.prototype.toString.call(value); //"[object Date]"
    type = valueClass.split(" ")[1].split(""); //[ 'D', 'a', 't', 'e', ']' ] 截取类型并转换为数组
    type.pop(); //[ 'D', 'a', 't', 'e' ],去掉数组最后的右括号"]"
    return type.join("").toLowerCase(); //[ 'D', 'a', 't', 'e' ] => "Date" => "date" 数组转小写字符串
  }
}

// 测试
console.info(getType(null)); // null
console.info(getType(undefined)); // undefined
console.info(getType(100)); // number
console.info(getType("abc")); // string
console.info(getType(true)); // boolean
console.info(getType(Symbol())); // symbol
console.info(getType({})); // object
console.info(getType([])); // array
console.info(getType(() => {})); // function
console.info(getType(new Date())); // date
console.info(getType(new RegExp(""))); // regexp
console.info(getType(new Map())); // map
console.info(getType(new Set())); // set
console.info(getType(new WeakMap())); // weakmap
console.info(getType(new WeakSet())); // weakset
console.info(getType(new Error())); // error
console.info(getType(new Promise(() => {}))); // promise

测试结果:

结语

本文总结了前端常见的一些手写功能,你是不是全都掌握了呢,欢迎在评论区交流。

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏✍️评论支持一下博主~     

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/368617.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一篇文章认识Vue3

Vue 3 介绍 Vue3 于 2022 年 2 月 7 日星期一成为新的默认版本&#xff01; Vue3 性能更高&#xff0c;体积更小 Vue3 在经过一年的迭代后&#xff0c;越来越好用。 官方文档&#xff1a; vue3官方文档&#xff1a; vuejs.org/ [1] vue3中文文档&#xff1a; v3.cn.vuejs.org/ …

数据结构-day7

二叉树创建、遍历、计算结点、计算深度 head.h #include<stdio.h> #include<stdlib.h> #include<string.h>typedef char datatype; typedef struct Btree{datatype data;struct Btree *lchild;struct Btree *rchild; }*btree;btree create(); void insert_…

【数据结构】双向链表 超详细 (含:何时用一级指针或二级指针;指针域的指针是否要释放)

目录 一、简介 二. 双链表的实现 1.准备工作及其注意事项 1.1 先创建三个文件 1.2 注意事项&#xff1a;帮助高效记忆 1.3 关于什么时候 用 一级指针接收&#xff0c;什么时候用 二级指针接收&#xff1f; 1.4 释放节点时&#xff0c;要将节点地址 置为NULL&#xff0…

如何过滤离线logcat日志文件?

1.需求&#xff1a; How did Android Studio Logcat to read the files which have save in logcat? I saved some logs and would like to open them with Android Studio - Logcat interface and be able to see the colours and apply some filters just as if the pho…

T113-Pro的buildroot添加gdisk ( GPT disks )出现gptfdisk needs a toolchain w/ C++的解决方法

问题背景&#xff1a; 最近入手了百问网的全志T113-Pro&#xff0c;用Emmc启动发现一张32GB的SD卡在烧录了百问网镜像 100ask-t113-pro_sdcard.img 的系统后&#xff0c;仅有200多M的存储空间。第一时间上百问网论坛看是否有板友也出现类似情况&#xff0c;发现了一个帖子正是描…

7.NFS服务器

目录 1. 简介 1.1. NFS背景介绍 1.2. 生产应用场景 2. NFS工作原理 2.1. 示例图 2.2. 流程 3. NFS的使用 3.1. 安装 3.2. 配置文件 3.3. 主配置文件分析 3.4. 实验1 3.5. NFS账户映射 3.5.1. 实验2&#xff1a; 3.5.2. 实验3 4. autofs自动挂载服务 4.1. 产生原…

Transformer 代码补充

本文是对Transformer - Attention is all you need 论文阅读-CSDN博客以及【李宏毅机器学习】Transformer 内容补充-CSDN博客的补充&#xff0c;是对相关代码的理解。 先说个题外话&#xff0c;在之前李宏毅老师的课程中提到multi-head attention是把得到的qkv分别乘上不同的矩…

030-安全开发-JS应用NodeJS指南原型链污染Express框架功能实现审计

030-安全开发-JS应用&NodeJS指南&原型链污染&Express框架&功能实现&审计 #知识点&#xff1a; 1、NodeJS-开发环境&功能实现 2、NodeJS-安全漏洞&案例分析 3、NodeJS-开发指南&特有漏洞 演示案例&#xff1a; ➢环境搭建-NodeJS-解析安装&…

常用换源总结

1.Ubuntu16.04更换国内源 在Ubuntu系统上使用apt-get install进行软件安装或更新的时候&#xff0c;由于使用的是国外源&#xff0c;导致下载速度很慢或者连接超时&#xff0c;需要更换下载源。 1.将系统原始的源文件进行备份 sudo cp /etc/apt/sources.list /etc/apt/source…

c语言--二进制和其他进制之间的转换

目录 一、前言二、二进制、十进制、十六进制、八进制的组成2.1二进制的组成2.2十进制的组成2.3八进制的组成2.4十六进制的组成 三、二进制转换为十进制3.1 二进制转换为十进制3.2十进制转换为二进制 四、二进制转八进制和十六进制4.1二进制转八进制4.2二进制转换为十六进制 五、…

【安装指南】maven下载、安装与配置详细教程

&#x1f33c;一、概述 maven功能与python的pip类似。 Apache Maven是一个用于软件项目管理和构建的强大工具。它是基于项目对象模型的&#xff0c;用于描述项目的构建配置和依赖关系。以下是一些关键的 Maven 特性和概念&#xff1a; POM&#xff08;Project Object Model&…

Mybatis基础教程及使用细节

本篇主要对Mybatis基础使用进行总结&#xff0c;包括Mybatis的基础操作&#xff0c;使用注解进行增删改查的练习&#xff1b;详细介绍xml映射文件配置过程并且使用xml映射文件进行动态sql语句进行条件查询&#xff1b;为了简化java开发提高效率&#xff0c;介绍一下依赖&#x…

安科瑞电气火灾监控系统在海尔(合肥)创新产业园一期厂房改扩建项目的设计与应用

摘要&#xff1a;介绍海尔&#xff08;合肥&#xff09;创新产业园一期厂房改扩建项目采用安科瑞剩余电流式电气火灾探测器&#xff0c;就地组网方式&#xff0c;通过现场总线通讯远传至后台&#xff0c;从而实现剩余电流式电气火灾监控系统的搭建&#xff0c;完成对现场配电回…

万户 ezOFFICE wpsservlet SQL注入漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

微信小程序应用商店源码系统 带完整的安装代码包以及搭建教程

随着微信小程序的普及&#xff0c;越来越多的企业和开发者开始关注小程序的开发与运营。为了满足市场需求&#xff0c;小编给大家分享一款微信小程序应用商店源码系统。该系统集成了完整的安装代码包&#xff0c;方便用户快速搭建自己的小程序应用商店。 以下是部分代码示例&a…

typedef

typedef typedef &#xff0c;type表示类型&#xff0c; def就是define&#xff0c; 定义的意思。所以&#xff0c;根据名字我们就可以知道typedef就是类型定义的意思。可以对一个类型进行重新定义。 一般对一个类型重新定义都是这种形式: typedef 类型 重定义 如&#xff…

web学习笔记(十九)

目录 1.作用域 1.1作用域的概念 1.2作用域的分类 1.2.1全局作用域 1.2.2局部作用域 1.2.3块级作用域&#xff08;ES6新增 &#xff09; 2.变量作用域 2.1全局变量 2.2局部变量 3.作用域链 3.1作用域链的定义 4.垃圾回收机制 4.1定义 4.2如何避免内存泄漏 5.预…

1895_分离进程的能力

1895_分离进程的能力 全部学习汇总&#xff1a; g_unix: UNIX系统学习笔记 (gitee.com) 有些理念可能在控制类的嵌入式系统中不好实施&#xff0c;尤其是没有unix这样的系统搭载的情况下。如果是考虑在RTOS的基础上看是否有一些理念可以做尝试&#xff0c;我觉得还是可以有一定…

Android: 深入理解 ‘companion object {}‘

Android: 深入理解 ‘companion object {}’ Kotlin是一种现代的、静态类型的编程语言&#xff0c;它在设计时充分考虑了开发者的生产力和代码的可读性。其中一个独特的特性就是companion object。在本篇博客中&#xff0c;我们将深入探讨这个特性&#xff0c;理解它的工作原理…

一款轻量级、高性能、功能强大的内网穿透代理服务器

简介 nps是一款轻量级、高性能、功能强大的内网穿透代理服务器。目前支持tcp、udp流量转发&#xff0c;可支持任何tcp、udp上层协议&#xff08;访问内网网站、本地支付接口调试、ssh访问、远程桌面&#xff0c;内网dns解析等等……&#xff09;&#xff0c;此外还支持内网htt…
最新文章