前端一面手写面试题总结
创始人
2024-06-02 18:34:05
0

使用Promise封装AJAX请求

// promise 封装实现:
function getJSON(url) {// 创建一个 promise 对象let promise = new Promise(function(resolve, reject) {let xhr = new XMLHttpRequest();// 新建一个 http 请求xhr.open("GET", url, true);// 设置状态的监听函数xhr.onreadystatechange = function() {if (this.readyState !== 4) return;// 当请求成功或失败时,改变 promise 的状态if (this.status === 200) {resolve(this.response);} else {reject(new Error(this.statusText));}};// 设置错误监听函数xhr.onerror = function() {reject(new Error(this.statusText));};// 设置响应的数据类型xhr.responseType = "json";// 设置请求头信息xhr.setRequestHeader("Accept", "application/json");// 发送 http 请求xhr.send(null);});return promise;
}

实现一个add方法完成两个大数相加

// 题目
let a = "9007199254740991";
let b = "1234567899999999999";function add(a ,b){//...
}

实现代码如下:

function add(a ,b){//取两个数字的最大长度let maxLength = Math.max(a.length, b.length);//用0去补齐长度a = a.padStart(maxLength , 0);//"0009007199254740991"b = b.padStart(maxLength , 0);//"1234567899999999999"//定义加法过程中需要用到的变量let t = 0;let f = 0;   //"进位"let sum = "";for(let i=maxLength-1 ; i>=0 ; i--){t = parseInt(a[i]) + parseInt(b[i]) + f;f = Math.floor(t/10);sum = t%10 + sum;}if(f!==0){sum = '' + f + sum;}return sum;
}

实现双向数据绑定

let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// 数据劫持
Object.defineProperty(obj, 'text', {configurable: true,enumerable: true,get() {console.log('获取数据了')},set(newVal) {console.log('数据更新了')input.value = newValspan.innerHTML = newVal}
})
// 输入监听
input.addEventListener('keyup', function(e) {obj.text = e.target.value
})

实现发布-订阅模式

class EventCenter{// 1. 定义事件容器,用来装事件数组let handlers = {}// 2. 添加事件方法,参数:事件名 事件方法addEventListener(type, handler) {// 创建新数组容器if (!this.handlers[type]) {this.handlers[type] = []}// 存入事件this.handlers[type].push(handler)}// 3. 触发事件,参数:事件名 事件参数dispatchEvent(type, params) {// 若没有注册该事件则抛出错误if (!this.handlers[type]) {return new Error('该事件未注册')}// 触发事件this.handlers[type].forEach(handler => {handler(...params)})}// 4. 事件移除,参数:事件名 要删除事件,若无第二个参数则删除该事件的订阅和发布removeEventListener(type, handler) {if (!this.handlers[type]) {return new Error('事件无效')}if (!handler) {// 移除事件delete this.handlers[type]} else {const index = this.handlers[type].findIndex(el => el === handler)if (index === -1) {return new Error('无该绑定事件')}// 移除事件this.handlers[type].splice(index, 1)if (this.handlers[type].length === 0) {delete this.handlers[type]}}}
}

手写节流函数

函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

// 函数节流的实现;
function throttle(fn, delay) {let curTime = Date.now();return function() {let context = this,args = arguments,nowTime = Date.now();// 如果两次时间间隔超过了指定时间,则执行函数。if (nowTime - curTime >= delay) {curTime = Date.now();return fn.apply(context, args);}};
}

Promise并行限制

就是实现有并行限制的Promise调度器问题

class Scheduler {constructor() {this.queue = [];this.maxCount = 2;this.runCounts = 0;}add(promiseCreator) {this.queue.push(promiseCreator);}taskStart() {for (let i = 0; i < this.maxCount; i++) {this.request();}}request() {if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {return;}this.runCounts++;this.queue.shift()().then(() => {this.runCounts--;this.request();});}
}const timeout = time => new Promise(resolve => {setTimeout(resolve, time);
})const scheduler = new Scheduler();const addTask = (time,order) => {scheduler.add(() => timeout(time).then(()=>console.log(order)))
}addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
scheduler.taskStart()
// 2
// 3
// 1
// 4

参考 前端进阶面试题详细解答

Promise.all

Promise.all是支持链式调用的,本质上就是返回了一个Promise实例,通过resolvereject来改变实例状态。

Promise.myAll = function(promiseArr) {return new Promise((resolve, reject) => {const ans = [];let index = 0;for (let i = 0; i < promiseArr.length; i++) {promiseArr[i].then(res => {ans[i] = res;index++;if (index === promiseArr.length) {resolve(ans);}}).catch(err => reject(err));}})
}

实现浅拷贝

浅拷贝是指,一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值,如果是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。

(1)Object.assign()

Object.assign()是ES6中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target, source_1, ···),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。

注意:

  • 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。
  • 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
  • 因为nullundefined 不能转化为对象,所以第一个参数不能为nullundefined,会报错。
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);  
console.log(target);  // {a: 1, b: 2, c: 3}

(2)扩展运算符

使用扩展运算符可以在构造字面量对象的时候,进行属性的拷贝。语法:let cloneObj = { ...obj };

let obj1 = {a:1,b:{c:1}}
let obj2 = {...obj1};
obj1.a = 2;
console.log(obj1); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj1.b.c = 2;
console.log(obj1); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}

(3)数组方法实现数组浅拷贝

1)Array.prototype.slice
  • slice()方法是JavaScript数组的一个方法,这个方法可以从已有数组中返回选定的元素:用法:array.slice(start, end),该方法不会改变原始数组。
  • 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
let arr = [1,2,3,4];
console.log(arr.slice()); // [1,2,3,4]
console.log(arr.slice() === arr); //false
2)Array.prototype.concat
  • concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
  • 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
let arr = [1,2,3,4];
console.log(arr.concat()); // [1,2,3,4]
console.log(arr.concat() === arr); //false

(4)手写实现浅拷贝

// 浅拷贝的实现;function shallowCopy(object) {// 只拷贝对象if (!object || typeof object !== "object") return;// 根据 object 的类型判断是新建一个数组还是对象let newObject = Array.isArray(object) ? [] : {};// 遍历 object,并且判断是 object 的属性才拷贝for (let key in object) {if (object.hasOwnProperty(key)) {newObject[key] = object[key];}}return newObject;
}// 浅拷贝的实现;function shallowCopy(object) {// 只拷贝对象if (!object || typeof object !== "object") return;// 根据 object 的类型判断是新建一个数组还是对象let newObject = Array.isArray(object) ? [] : {};// 遍历 object,并且判断是 object 的属性才拷贝for (let key in object) {if (object.hasOwnProperty(key)) {newObject[key] = object[key];}}return newObject;
}// 浅拷贝的实现;
function shallowCopy(object) {// 只拷贝对象if (!object || typeof object !== "object") return;// 根据 object 的类型判断是新建一个数组还是对象let newObject = Array.isArray(object) ? [] : {};// 遍历 object,并且判断是 object 的属性才拷贝for (let key in object) {if (object.hasOwnProperty(key)) {newObject[key] = object[key];}}return newObject;
}

手写 new 操作符

在调用 new 的过程中会发生以上四件事情:

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

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

(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)

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

function objectFactory() {let newObject = null;let constructor = Array.prototype.shift.call(arguments);let result = null;// 判断参数是否是一个函数if (typeof constructor !== "function") {console.error("type error");return;}// 新建一个空对象,对象的原型为构造函数的 prototype 对象newObject = Object.create(constructor.prototype);// 将 this 指向新建对象,并执行函数result = constructor.apply(newObject, arguments);// 判断返回对象let flag = result && (typeof result === "object" || typeof result === "function");// 判断返回结果return flag ? result : newObject;
}
// 使用方法
objectFactory(构造函数, 初始化参数);

Object.assign

Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象(请注意这个操作是浅拷贝)

Object.defineProperty(Object, 'assign', {value: function(target, ...args) {if (target == null) {return new TypeError('Cannot convert undefined or null to object');}// 目标对象需要统一是引用数据类型,若不是会自动转换const to = Object(target);for (let i = 0; i < args.length; i++) {// 每一个源对象const nextSource = args[i];if (nextSource !== null) {// 使用for...in和hasOwnProperty双重判断,确保只拿到本身的属性、方法(不包含继承的)for (const nextKey in nextSource) {if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {to[nextKey] = nextSource[nextKey];}}}}return to;},// 不可枚举enumerable: false,writable: true,configurable: true,
})

实现 add(1)(2)(3)

函数柯里化概念: 柯里化(Currying)是把接受多个参数的函数转变为接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。

1)粗暴版

function add (a) {
return function (b) {return function (c) {return a + b + c;}
}
}
console.log(add(1)(2)(3)); // 6

2)柯里化解决方案

  • 参数长度固定
var add = function (m) {var temp = function (n) {return add(m + n);}temp.toString = function () {return m;}return temp;
};
console.log(add(3)(4)(5)); // 12
console.log(add(3)(6)(9)(25)); // 43

对于add(3)(4)(5),其执行过程如下:

  1. 先执行add(3),此时m=3,并且返回temp函数;

  2. 执行temp(4),这个函数内执行add(m+n),n是此次传进来的数值4,m值还是上一步中的3,所以add(m+n)=add(3+4)=add(7),此时m=7,并且返回temp函数

  3. 执行temp(5),这个函数内执行add(m+n),n是此次传进来的数值5,m值还是上一步中的7,所以add(m+n)=add(7+5)=add(12),此时m=12,并且返回temp函数

  4. 由于后面没有传入参数,等于返回的temp函数不被执行而是打印,了解JS的朋友都知道对象的toString是修改对象转换字符串的方法,因此代码中temp函数的toString函数return m值,而m值是最后一步执行函数时的值m=12,所以返回值是12。

  • 参数长度不固定
function add (...args) {//求和return args.reduce((a, b) => a + b)
}
function currying (fn) {let args = []return function temp (...newArgs) {if (newArgs.length) {args = [...args,...newArgs]return temp} else {let val = fn.apply(this, args)args = [] //保证再次调用时清空return val}}
}
let addCurry = currying(add)
console.log(addCurry(1)(2)(3)(4, 5)())  //15
console.log(addCurry(1)(2)(3, 4, 5)())  //15
console.log(addCurry(1)(2, 3, 4, 5)())  //15

字符串解析问题

var a = {b: 123,c: '456',e: '789',
}
var str=`a{a.b}aa{a.c}aa {a.d}aaaa`;
// => 'a123aa456aa {a.d}aaaa'

实现函数使得将str字符串中的{}内的变量替换,如果属性不存在保持原样(比如{a.d}

类似于模版字符串,但有一点出入,实际上原理大差不差

const fn1 = (str, obj) => {let res = '';// 标志位,标志前面是否有{let flag = false;let start;for (let i = 0; i < str.length; i++) {if (str[i] === '{') {flag = true;start = i + 1;continue;}if (!flag) res += str[i];else {if (str[i] === '}') {flag = false;res += match(str.slice(start, i), obj);}}}return res;
}
// 对象匹配操作
const match = (str, obj) => {const keys = str.split('.').slice(1);let index = 0;let o = obj;while (index < keys.length) {const key = keys[index];if (!o[key]) {return `{${str}}`;} else {o = o[key];}index++;}return o;
}

使用 setTimeout 实现 setInterval

setInterval 的作用是每隔一段指定时间执行一个函数,但是这个执行不是真的到了时间立即执行,它真正的作用是每隔一段时间将事件加入事件队列中去,只有当当前的执行栈为空的时候,才能去从事件队列中取出事件执行。所以可能会出现这样的情况,就是当前执行栈执行的时间很长,导致事件队列里边积累多个定时器加入的事件,当执行栈结束的时候,这些事件会依次执行,因此就不能到间隔一段时间执行的效果。

针对 setInterval 的这个缺点,我们可以使用 setTimeout 递归调用来模拟 setInterval,这样我们就确保了只有一个事件结束了,我们才会触发下一个定时器事件,这样解决了 setInterval 的问题。

实现思路是使用递归函数,不断地去执行 setTimeout 从而达到 setInterval 的效果

function mySetInterval(fn, timeout) {// 控制器,控制定时器是否继续执行var timer = {flag: true};// 设置递归函数,模拟定时器执行。function interval() {if (timer.flag) {fn();setTimeout(interval, timeout);}}// 启动定时器setTimeout(interval, timeout);// 返回控制器return timer;
}

封装异步的fetch,使用async await方式来使用

(async () => {class HttpRequestUtil {async get(url) {const res = await fetch(url);const data = await res.json();return data;}async post(url, data) {const res = await fetch(url, {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(data)});const result = await res.json();return result;}async put(url, data) {const res = await fetch(url, {method: 'PUT',headers: {'Content-Type': 'application/json'},data: JSON.stringify(data)});const result = await res.json();return result;}async delete(url, data) {const res = await fetch(url, {method: 'DELETE',headers: {'Content-Type': 'application/json'},data: JSON.stringify(data)});const result = await res.json();return result;}}const httpRequestUtil = new HttpRequestUtil();const res = await httpRequestUtil.get('http://golderbrother.cn/');console.log(res);
})();

实现Event(event bus)

event bus既是node中各个模块的基石,又是前端组件通信的依赖手段之一,同时涉及了订阅-发布设计模式,是非常重要的基础。

简单版:

class EventEmeitter {constructor() {this._events = this._events || new Map(); // 储存事件/回调键值对this._maxListeners = this._maxListeners || 10; // 设立监听上限}
}// 触发名为type的事件
EventEmeitter.prototype.emit = function(type, ...args) {let handler;// 从储存事件键值对的this._events中获取对应事件回调函数handler = this._events.get(type);if (args.length > 0) {handler.apply(this, args);} else {handler.call(this);}return true;
};// 监听名为type的事件
EventEmeitter.prototype.addListener = function(type, fn) {// 将type事件以及对应的fn函数放入this._events中储存if (!this._events.get(type)) {this._events.set(type, fn);}
};

面试版:

class EventEmeitter {constructor() {this._events = this._events || new Map(); // 储存事件/回调键值对this._maxListeners = this._maxListeners || 10; // 设立监听上限}
}// 触发名为type的事件
EventEmeitter.prototype.emit = function(type, ...args) {let handler;// 从储存事件键值对的this._events中获取对应事件回调函数handler = this._events.get(type);if (args.length > 0) {handler.apply(this, args);} else {handler.call(this);}return true;
};// 监听名为type的事件
EventEmeitter.prototype.addListener = function(type, fn) {// 将type事件以及对应的fn函数放入this._events中储存if (!this._events.get(type)) {this._events.set(type, fn);}
};// 触发名为type的事件
EventEmeitter.prototype.emit = function(type, ...args) {let handler;handler = this._events.get(type);if (Array.isArray(handler)) {// 如果是一个数组说明有多个监听者,需要依次此触发里面的函数for (let i = 0; i < handler.length; i++) {if (args.length > 0) {handler[i].apply(this, args);} else {handler[i].call(this);}}} else {// 单个函数的情况我们直接触发即可if (args.length > 0) {handler.apply(this, args);} else {handler.call(this);}}return true;
};// 监听名为type的事件
EventEmeitter.prototype.addListener = function(type, fn) {const handler = this._events.get(type); // 获取对应事件名称的函数清单if (!handler) {this._events.set(type, fn);} else if (handler && typeof handler === "function") {// 如果handler是函数说明只有一个监听者this._events.set(type, [handler, fn]); // 多个监听者我们需要用数组储存} else {handler.push(fn); // 已经有多个监听者,那么直接往数组里push函数即可}
};EventEmeitter.prototype.removeListener = function(type, fn) {const handler = this._events.get(type); // 获取对应事件名称的函数清单// 如果是函数,说明只被监听了一次if (handler && typeof handler === "function") {this._events.delete(type, fn);} else {let postion;// 如果handler是数组,说明被监听多次要找到对应的函数for (let i = 0; i < handler.length; i++) {if (handler[i] === fn) {postion = i;} else {postion = -1;}}// 如果找到匹配的函数,从数组中清除if (postion !== -1) {// 找到数组对应的位置,直接清除此回调handler.splice(postion, 1);// 如果清除后只有一个函数,那么取消数组,以函数形式保存if (handler.length === 1) {this._events.set(type, handler[0]);}} else {return this;}}
};

实现具体过程和思路见实现event

解析 URL Params 为对象

let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 结果
{ user: 'anonymous',id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型city: '北京', // 中文需解码enabled: true, // 未指定值得 key 约定为 true
}
*/
function parseParam(url) {const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中let paramsObj = {};// 将 params 存到对象中paramsArr.forEach(param => {if (/=/.test(param)) { // 处理有 value 的参数let [key, val] = param.split('='); // 分割 key 和 valueval = decodeURIComponent(val); // 解码val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值paramsObj[key] = [].concat(paramsObj[key], val);} else { // 如果对象没有这个 key,创建 key 并设置值paramsObj[key] = val;}} else { // 处理没有 value 的参数paramsObj[param] = true;}})return paramsObj;
}

验证是否是身份证

function isCardNo(number) {var regx = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;return regx.test(number);
}

实现观察者模式

观察者模式(基于发布订阅模式) 有观察者,也有被观察者

观察者需要放到被观察者中,被观察者的状态变化需要通知观察者 我变化了 内部也是基于发布订阅模式,收集观察者,状态变化后要主动通知观察者

class Subject { // 被观察者 学生constructor(name) {this.state = 'happy'this.observers = []; // 存储所有的观察者}// 收集所有的观察者attach(o){ // Subject. prototype. attchthis.observers.push(o)}// 更新被观察者 状态的方法setState(newState) {this.state = newState; // 更新状态// this 指被观察者 学生this.observers.forEach(o => o.update(this)) // 通知观察者 更新它们的状态}
}class Observer{ // 观察者 父母和老师constructor(name) {this.name = name}update(student) {console.log('当前' + this.name + '被通知了', '当前学生的状态是' + student.state)}
}let student = new Subject('学生'); let parent = new Observer('父母'); 
let teacher = new Observer('老师'); // 被观察者存储观察者的前提,需要先接纳观察者
student. attach(parent); 
student. attach(teacher); 
student. setState('被欺负了');

实现数组的filter方法

Array.prototype._filter = function(fn) {if (typeof fn !== "function") {throw Error('参数必须是一个函数');}const res = [];for (let i = 0, len = this.length; i < len; i++) {fn(this[i]) && res.push(this[i]);}return res;
}

实现千位分隔符

// 保留三位小数
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); // return '123,456,789'
parseToMoney(1087654.321); // return '1,087,654.321'
function parseToMoney(num) {num = parseFloat(num.toFixed(3));let [integer, decimal] = String.prototype.split.call(num, '.');integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');return integer + '.' + (decimal ? decimal : '');
}

正则表达式(运用了正则的前向声明和反前向声明):

function parseToMoney(str){// 仅仅对位置进行匹配let re = /(?=(?!\b)(\d{3})+$)/g; return str.replace(re,','); 
}

相关内容

热门资讯

演出节目串词2文 演出节目串词2文(男)尊敬的领导、老师、亲爱的同学们。 (合)大家好。 (女)当鲜红的太阳跃上地平线...
庆祝百岁老人生日的致辞 庆祝百岁老人生日的致辞范文(精选5篇)  在生活、工作和学习中,大家总免不了要接触或使用致辞吧,致辞...
《夏有乔木雅望天堂》的经典台... 《夏有乔木雅望天堂》的经典台词  《夏有乔木雅望天堂》经典台词一  1. 一个等了,却等得太早,一个...
中秋节的主持词 中秋节的主持词  主持人在台上表演的灵魂就表现在主持词中。在当下的中国社会,很多场合都需要主持人活跃...
无间道台词 无间道台词  说好了三年,三年之后又三年,三年之后又三年,都快十年了,老大!  出来跑,迟早要还的。...
六十岁生日宴会致辞 六十岁生日宴会致辞(通用10篇)  在学习、工作或生活中,要用到致辞的情况还是蛮多的,致辞讲求条理性...
终极三国的经典台词 终极三国的经典台词  1.如此如此,这般这般~  2.我姓刘名备,字玄德,是中山靖王的儿子,因为家道...
团代会主持词 团代会主持词  利用在中国拥有几千年文化的诗词能够有效提高主持词的感染力。现今社会在不断向前发展,主...
《剑雨》经典台词盘点 《剑雨》经典台词盘点  1、生未必乐,死未必苦。  2、未来已成现在,现在已成过去,随心而去。  3...
幼儿园园长开园致辞 幼儿园园长开园致辞  在日常学习、工作和生活中,大家都不可避免地会接触到致辞吧,致辞是指在仪式上所讲...
辩论赛主持人主持词开场白 辩论赛主持人主持词开场白  辩论赛怎么能没有我们主持人呢?下面是小编搜集整理的辩论赛主持人主持词开场...
李白凤求凰特殊台词 李白凤求凰特殊台词  在王者荣耀中每个英雄人物都有台词,那么李白凤求凰特殊台词是什么呢?以下是小编整...
学生读书交流会主持词 学生读书交流会主持词  主持词要把握好吸引观众、导入主题、创设情境等环节以吸引观众。在当下的中国社会...
晚会的闭幕词 晚会的闭幕词(精选16篇)  主持词是主持活动的必备稿子,是活跃气氛,引导活动进行的存在,下面是小编...
阿甘正传电影经典台词 阿甘正传电影经典台词大全  《阿甘正传》给我们展现了一个虽然智商只有75,却是忠诚、守信、执着、友善...
致青春经典台词 致青春经典台词  1、青春是有限的,不能在犹豫和观望中度过。  2、很多东西就像气球一样,看上去很美...
追悼会主持词 追悼会主持词  什么是追悼会  追悼会,为悼念死者而召开的会议。有些在死者遗体所在地举行,有些在殡仪...
幼儿园大班新年联欢会主持词   主持人:左XX  开场:  左:亲爱的老师、同学们:  合:大家好!  彭:20XX年马上就要过...
新春的主持稿 新春的主持稿  在日常生活和工作中,需要使用主持稿的情况越来越多,主持稿是主持人在会议或是节目当中串...
五四青年节的致辞 五四青年节的致辞(通用20篇)  在平日的学习、工作和生活里,大家总少不了要接触或使用致辞吧,致辞是...