function fn(){
let a = 1;
function fn2(){
return a += 1;
console.log(a)
}
return fn2;
}
let xxx = fn();
xxx();
一个函数用到了它外部的变量,那这个函数加那个变量就是闭包。
比如上面代码的fn2的函数用到了它外部fn函数的变量,
那么function fn2( ){ console.log(a) } 和 let a = 1 就是闭包。
不合理的使用会造成内存泄漏,由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏。解决方法是,在退出函数之前,将不在使用的局部变量全部删除。
call() / apply()
指定函数中的this为第一个参数指定的对象,
如果函数执行需要传参数, call是依次传递, apply需要封装成数组传递
call() 语法: function.call(thisArg, p1, p2, …)
function fn(a,b){
console.log('name:'+a,'age:'+b)
}
let xxx={'name':'kasan','age':18}
fn.call(xxx, xxx.name, xxx.age) //name:kasan age:18
apply() 语法: function.call(thisArg, [p1, p2, …] )
function fn(a,b){
console.log('name:'+a,'age:'+b)
}
let xxx={'name':'kasan','age':18}
fn.apply(xxx,[xxx.name,xxx.age]);//name:kasan age:18
bind的用法与call,apply的用法基本一致,区别是bind函数不会像call和apply一样立即调用执行,而是会返回一个新的函数,想执行的时候再执行。
function xxx(a1,b1){
console.log(a1+b1)
}
let obj = {a:11,b:22}
let xxx1 = xxx.bind(obj,obj.a,obj.b)
xxx1() //33
//如果是call
let xxx2 = xxx.call(obj,obj.a,obj.b)
console.log(xxx2) //undefined
如何实现数组去重? 假设有数组 array = [1,5,2,3,4,2,3,1,3,4] 你要写一个函数 unique,使得 unique(array) 的值为 [1,5,2,3,4] 也就是把重复的值都去掉,只保留不重复的值。
//不用set
array = [1,5,2,3,4,2,3,1,3,4]
function unique(arr){
let arr1 = [];
for(let i=0;i<arr.length;i++){
for(let i1=i+1;i1<arr.length;i1++){
let a = arr[i]
let b = arr[i1]
if( a===b ){
delete arr[i1]
}
}
}
for(let i2=0;i2<arr.length;i2++){
if(typeof arr[i2] === 'number'){
arr1.push(arr[i2])
}
}
return console.log(arr1);
}
unique(array)
//set()
array = [1,5,2,3,4,2,3,1,3,4]
console.log(new Set(array))
//map
//主要思路:创建一个空Map,遍历原始数组,把数组的每一个元素作为key存到Map中,因为Map中不会出现相同的key值,所以最终得到的Map中的所有key值就是去重后的结果。
array = [1,5,2,3,4,2,3,1,3,4]
function fn(arr){
let hashMap = new Map();
let result = [];
for(let i = 0; i < arr.length; i++ ){
if(hashMap.has(arr[i])){ //如果hashMap没有arr[i],就添加key为arr[i],值为true表示该值是重复的
hashMap.set(arr[i],true)
}else{ //反之为false 表示不重复的值,然后push到result里,返回result。
hashMap.set(arr[i],false) //
result.push(arr[i])
}
}
console.log(hashMap)
return console.log(result);
}
fn(array)
概念: 事件委托也叫事件代理,“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown…)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
假设现在要监听100个按钮,效果是:点击不同的按钮时打印出按钮的编号
我们可以通过监听所有button元素的父元素div1,当target为button时,打印出编号
有一些html元素默认的行为,比如说a标签,点击后有跳转动作;form表单中的submit类型的input有一个默认提交跳转事件;reset类型的input有重置表单行为。
如果你想阻止这些浏览器默认行为,JavaScript为你提供了方法。 如下代码:
let aaa = document.getElementsByTagName("a")[0];
$aaa.onclick = function(e){
alert("跳转动作被我阻止了")
e.preventDefault();
//默认事件没有了。
}
使用 e.stopPropagation()
在支持 addEventListener() 的浏览器中,可以调用事件对象的一个 stopPropagation() 方法已阻止事件的继续传播。如果在同一对象上定义了其他处理程序,剩下的处理程序将依旧被调用,但调用 stopPropagation() 方法可以在事件传播期间的任何时间调用,它能工作在捕获阶段、事件目标本身中和冒泡阶段。
1.可以只用构造函数,原理是在每个Child1的实例加上Parent1的值和方法,这样做是可以实现继承的功能,但是会大量浪费内存,造成性能下降。
function Parent1(name) {
this.name = name || '俊宇';
this.play = [1, 2, 3]
this.ID = '1';
this.xxx = function(any){
console.log(any+',居然可以')
};
}
function Child1(name,like) {
Parent1.call(this,name)
this.like = like ;
this.doing = function(something){
console.log(this.name+'在做:'+something+'I Like:'+like)
}
}
let s1 = new Parent1('P1');
let s2 = new Child1('','apple')
let s3 = new Child1('小明','game')
s2.play.push(4)
console.log(s2.play,s3.play)
s2.ID = '2';
console.log(s2.ID,s3.ID)
s2.xxx('嘻嘻')
s3.xxx('哈哈')
s2.xxx = function(something){console.log(something+',改写了')}
s2.xxx('嘻嘻')
s3.xxx('哈哈')
s2.doing('玩游戏')
s3.doing('打篮球')
console.log(s1.constructor)
console.log(s2.constructor)
console.log(s3.constructor)
console.log(s1)
console.log(s2)
console.log(s3)
所以,需要把复用的方法放在Parent1的prototype上,
然后用create(Parent1.prototype)以Parent1.prototype作为原型构建一个对象返回。
作为中间对象,隔离开子类原型和父类原型。 然后再把constructor指向Child1
就可以比较完美地完成继承。
function Parent1(name) {
this.name = name || '俊宇';
this.play = [1, 2, 3]
this.ID = '1';
}
Parent1.prototype.xxx = function(any){
console.log(any+',居然可以')
}
function Child1(name,like) {
Parent1.call(this,name)
this.like = like ;
this.doing = function(something){
console.log(this.name+'在做:'+something+'I Like:'+like)
}
}
Child1.prototype = Object.create(Parent1.prototype);
Child1.prototype.constructor = Child1;
let s1 = new Parent1('P1');
let s2 = new Child1('','apple')
let s3 = new Child1('小明','game')
s2.play.push(4)
console.log(s2.play,s3.play)
s2.ID = '2';
console.log(s2.ID,s3.ID)
s2.xxx('嘻嘻')
s3.xxx('哈哈')
s2.xxx = function(something){console.log(something+',改写了')}
s2.xxx('嘻嘻')
s3.xxx('哈哈')
s2.doing('玩游戏')
s3.doing('打篮球')
console.log(s1.constructor)
console.log(s2.constructor)
console.log(s3.constructor)
console.log(s1)
console.log(s2)
console.log(s3)
直接子类 extends 父类就可以了,函数和值都可以使用访问。
class Parent{
constructor(name){
this.name = name || '俊宇';
this.play = [1, 2, 3];
this.ID = '1';
}
xxx(any){
console.log(any+',居然可以')
}
}
class Child extends Parent{
constructor(name,like){
super(name);
this.like = like;
}
doing(something){
console.log(this.name+'在做:'+something+'I Like:'+this.like)
}
}
let s1 = new Parent('P1');
let s2 = new Child('','apple')
let s3 = new Child('小明','game')
s2.play.push(4)
console.log(s2.play,s3.play)
s2.ID = '2';
console.log(s2.ID,s3.ID)
s2.xxx('嘻嘻')
s3.xxx('哈哈')
s2.xxx = function(something){console.log(something+',改写了')}
s2.xxx('嘻嘻')
s3.xxx('哈哈')
s2.doing('玩游戏')
s3.doing('打篮球')
console.log(s1.constructor)
console.log(s2.constructor)
console.log(s3.constructor)
console.log(s1)
console.log(s2)
console.log(s3)
array = [2,1,5,3,8,4,9,5]
function sort(arr){
for(let i = 0; i<arr.length; i++){
for(let k = i+1; k<arr.length; k++){
if(arr[i] > arr[k] ){
let a1 = arr[i]
let b1 = arr[k]
arr[i] = b1;
arr[k] = a1;
}
}
}
console.log(arr)
}
sort(array)
//Array的sort() ——>console.log(array.sort())
promise的作用是用来解决异步操作时,成功或失败回调的问题。
如果不用Promise,可能会面临一些问题:
Promise接受两个回调参数,第一个参数resolve,当异步操作成功时会调用,它有一个参数用于传递异步操作成功的结果。第二个参数reject,当异步操作失败时会调用,它有一个参数用于传递异步操作失败的信息。
let myPromise = new Promise(function(resolve, reject) {
... //异步操作
if( success ) {
resolve(value);
} else {
reject(error);
}
});
then() 方法返回一个 Promise 对象。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。
语法:
p.then((response)=>{}, (request)=>{});
例子:
let a = 1;
let promise = new Promise(function (resolve, reject) {
if (a == 10) {
resolve('成功')
} else {
reject("失败")
}
})
// promise.then(success,fail)
promise.then(res => {
console.log("成功调用", res);
}, err => {
console.log("失败调用", err);
})
一个脚本中有多个promise时,监控多个Promise对象
.all([p1,p2,p3,…])
有一个失败的请求,其他都失败;若需弥补这个缺陷,在每个Promise中增加Catch错误捕捉,且return出来;
返回结果的顺序按照参数的顺序进行;
语法:
Promise.all(iterable);
例子:
// promise.all([p1,p2,p3]):把promise打包,扔到一个数组里面,打包完还是一个promise对象
let p1 = new Promise((resolve, reject) => {
let time = Math.random() * 4000 + 1000;
setTimeout(() => {
console.log('P1完成');
resolve();
}, time)
})
let p2 = new Promise((resolve, reject) => {
let time = Math.random() * 4000 + 1000;
setTimeout(() => {
console.log('p2完成');
resolve()
}, time);
})
let p3 = new Promise((resolve, reject) => {
let time = Math.random() * 4000 + 1000;
setTimeout(() => {
console.log('p3完成');
reject()
}, time);
})
// 必须确保所有Promise 对象都是resolve状态
let p = Promise.all([p1, p2])
p.then(() => {
// p1 和 p2 全部执行完毕后,才会执行then方法里面的操作
console.log('全部执行完成');
})
假设有三个请求,分别请求三个文件,race有竞速的意思, 所以把返回的第一个响应作为结果,假设第三个请求最先响应,是请求失败的, 那么相当于全局都失败了,就会执行race失败的函数。
再假如第二个请求最先返回,是请求成功的,那么就是全局成功,执行race成功的函数
语法与all一样
例子:
let p1 = new Promise((resolve, reject) => {
let time = Math.random() * 4000 + 1000;
setTimeout(() => {
console.log('P1完成');
resolve();
}, time)
})
let p2 = new Promise((resolve, reject) => {
let time = Math.random() * 4000 + 1000;
setTimeout(() => {
console.log('p2完成');
resolve()
}, time);
})
let p3 = new Promise((resolve, reject) => {
let time = 1000 ;
setTimeout(() => {
console.log('p3完成');
reject()
}, time);
})
// 谁先执行完成,race就是谁的状态,去执行then,比如上面第一个执行完成的是
//p3,那么promise就是rejected状态,触发then的reject函数
let p = Promise.race([p1, p2,p3])
p.then(() => {
//
console.log('全部执行完成');
},()=>{console.log('未执行完成');})
定义:地址不完全相同的网站不能获取对方的数据内容
哪怕是域名,ip,端口,等等有不一样的地方,都不能获取对方不想被获取的数据
因为历史原因,当年的服务器和带宽都十分昂贵,一个服务器可以跑很多网站,所以浏览器需要定制一个策略来限制不同网站间的数据获取,
否则所有网站的数据都会被泄露,浏览器就变成一个纯纯的病毒。
数据可以被引用,但不能被获取
比如一个网站A的js文件,它可以被网站B下载,运行,但是如果网站A不想共享该文件,浏览器就不会把源码展示出来,这是浏览器为数据安全作出的隔离策略,叫同源策略。
简单来说,跨域就是为了突破浏览器的同源策略,实现主动共享数据的需求。
只需要在后台把需要共享的数据设置头为对方地址,对方就可以获取到这个数据。
比如我想共享数据给ip为127.0.0.1,端口为8889的网站好友的信息,那么我只需要在后台设置一个头,输入对方的地址就可以了,数据就定向共享出去了。
response.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:8889')
else if(path === '/friends.json'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/json;charset=utf-8')
response.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:8889') //localhost === 127.0.0.1
response.setHeader('Access-Control-Allow-Origin','http://localhost:8889')
response.write(fs.readFileSync('./public/friends.json'))
response.end()
}
在接收数据的网站写:AJAX请求对方的数据地址,然后打印出获取的数据
const request = new XMLHttpRequest()
request.open('GET', 'http://127.0.0.1:8888/friends.json')
request.onreadystatechange = ()=>{
if(request.readyState===4 && request.status === 200){
console.log(request.response)
}
}
request.send()
很不幸的是,IE 6,7,8,9并不支持CORS跨域
所以如果需要为IE做跨域,只能另辟蹊径,前端工程师想出了一种方法,把数据打包在js里,用js发送这些数据出去。
你需要先创建一个用来装数据分享出去的js文件
然后把需要共享的数据替换到js里,然后响应发送出去。
else if(path === '/friends.js'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
const string1 = fs.readFileSync('./public/friends.js').toString();
const data = fs.readFileSync('./public/friends.json').toString();
const string2 = string1.replace('',data)
response.write(string2)
response.end()
}
在接收数据的网站,创建一个script标签,把对方共享的数据地址写到script标签
然后把script标签加到body,打印出来,就获取到对方要共享的数据了
const script = document.createElement('script')
script.src = 'http://127.0.0.1:8888/friends.js'
script.onload = () => {
console.log(window.xxx)
}
document.body.appendChild(script)
到这里就完成了JSONP的基本应用
但是现在还有些问题,比如这样写的话,所有网站都可以获取到你要定向共享的数据
这样就没有意义了,所以我们需要定向共享的话,就需要做一个 referer 检查
假如访问方的地址不为:http://127.0.0.1:8889 ,就返回404。
如果是 http://127.0.0.1:8889 ,就返回共享的数据
这样可以做到定向访问数据,但是如果对方网站被攻陷,那么我们的共享数据也会被偷走
安全的上限是取决于该安全链条上最薄弱的一环。
所以后面需要添加更多的验证来提高安全水平。
if(request.headers['referer'].indexOf('http://127.0.0.1:8889')){
const string1 = fs.readFileSync('./public/friends.js').toString();
const data = fs.readFileSync('./public/friends.json').toString();
const string2 = string1.replace('',data)
response.write(string2)
response.end()
}else{
response.statusCode = 404;
response.end();
}
1990 年,第一个web浏览器诞生,Tim 以超文本语言 HTML 为基础在 NeXT 电脑上发明了最原始的 Web 浏览器。
1991 年,WWW诞生,这标志着前端技术的开始。
在那个前后端不分的年代,网站采用静态网页为人们传递信息,
画面简陋,观感差,优化用户看到的画面成为了一种隐性需求。
后来,
1994年哈肯·维姆·莱提出了CSS的最初建议。伯特·波斯(Bert Bos)当时正在设计一个叫做“Argo”的浏览器,他们决定一起合作设计CSS。
CSS就这样开始诞生,虽然一开始的CSS只支持一些字体的大小,字形,强调,颜色,字的距离等等这种简单的功能,但是在那个年代,我敢肯定是网页观感的巨大提升。
再后来,网页需要交互,JS诞生了,前后端需要分离,AJAX诞生了,
一个一个新颖的技术如雨后春笋般冒出。
到2022年,经过数十年的发展,前端工程师早成为程序开发的一条独立分支,
前端工程师,别称web前端开发攻城狮,是在2005年由淘宝发明出来的称呼。前端工程师通过前端技术完成界面设计,界面制作,用户交互,网站维护、网站优化等等。
通俗点讲,可以设计、制作网页,给网页加上各种各样的特效和功能。
前端需要学会哪些技术?
基础:html+css+javascript,html5+css3
进阶:各种框架,如Bootstrap,jquery,react,vue,angular等等
其他:http,一门后端语言,网站优化等等
我对前端的理解:在网络平台上,展示、传递信息给用户,或跟用户进行交流的界面,优化画面,在观感上给用户带来极高的体验。