Dieser Artikel organisiert hauptsächlich einige ähnliche Schlüsselwörter, Methoden und Konzepte in javascript
und teilt sie mit allen, in der Hoffnung, allen zu helfen.
Eine mit var deklarierte Variable hat ihren Gültigkeitsbereich innerhalb der Funktion, in der sich die Anweisung befindet, und Die Variable existiert. Das Hebephänomen
Variablen, die mit let deklariert wurden, haben ihren Gültigkeitsbereich innerhalb des Codeblocks, in dem sich die Anweisung befindet. Es gibt kein Variablenhebephänomen
Const verwenden Was deklariert wird, ist eine Konstante, und der Wert und die Adresse der Konstante auf dem Stapel können im später angezeigten Code nicht geändert werden
Eine mit function deklarierte Funktion hat ihren Gültigkeitsbereich wo sich die Anweisung innerhalb der Funktion befindet und es ein Funktionsförderungsphänomen gibt
var
//a. 变量提升 console.log(a) // => undefined var a = 123 //b. 作用域 function f() { var a = 123 console.log(a) // => 123 } console.log(a) // => a is not defined for (var i = 0; i < 10; i ++) {} console.log(i) // => 10
let
//a. 变量不提升 console.log(a) // => a is not defined let a = 123 //b. 作用域为所在代码块内 for (let i = 0; i < 10; i ++) {} console.log(i) // => i is not defined
const
//a. 不能修改的是栈内存在的值和地址 const a = 10 a = 20 // => Assignment to constant variable // 但是以下的赋值确是合法的 const a = { b: 20 } a.b = 30 console.log(a.b) // => 30
Funktion
//a. 函数提升 fn() // => 123 function fn() { return 123 } //b. 作用域 function fn() { function fn1 () { return 123456 } fn1() // => 123456 } fn1() // => fn1 is not defined
Klassische Interviewfragen
var a = 1 function fn() { if (!a) { var a = 123 } console.log(a) } fn() ?
// So drucken Sie 0 - 9 nacheinander aus
for (var i = 0; i < 10; i++) { setTimeout(function(){ console.log(i) }) }
function Foo() { getName = function(){ console.log("1"); }; return this; } Foo.getName = function() { console.log("2"); }; Foo.prototype.getName = function(){ console.log("3"); }; var getName = function() { console.log("4"); } function getName(){ console.log("5"); } Foo.getName(); ? getName(); ? Foo().getName(); ? getName(); ? new Foo.getName(); ? new Foo().getName(); ?
Antwort:
Frage 1
//我们把它执行顺序整理下 var a = 1 function fn() { var a = nudefined if (!a) { var a = 123 } console.log(a) } //所以 答案很明显 就是 123
Frage 2
for (var i = 0; i < 10; i++) { print(i) } function print(i) { // 把每个变量i值传进来,变成只可当前作用域访问的局部变量 setTimeout(function(){ console.log(i) }) } // 或者自执行函数简写 for (var i = 0; i < 10; i++) { (function(i){ setTimeout(function(){ console.log(i) }) })(i) }
Frage 3
// 我们整理下它的执行顺序 var getName = undefined function Foo() { getName = function(){ console.log("1"); }; return this; } function getName(){ console.log("5"); } Foo.getName = function() { console.log("2"); }; Foo.prototype.getName = function(){ console.log("3"); }; getName = function() { console.log("4"); } Foo.getName(); // 2 /* 函数也是对象, Foo.getName 相当于给 Foo这个对象添加了一个静态方法 getName,我们调用的其实是这个静态方法,并不是调用的我们实例化的 getName */ getName(); // 4 /* 按照上面的执行顺序,其实这个就很好理解了,因为 `getName = function() { console.log("4"); }` 是最后一个赋值, 执行的应该是这个函数 */ Foo().getName(); // 1 /* 这里为什么是 1 而不是我们想象的 3 呢? 问题就是出在 调用的是 Foo(); 并没有使用 new 这个关键字,所以那时候返回的 this 指向的并不是 Foo, 而是 window; 至于为什么不用 new 返回的 this 不指向 Foo, 这个随便去哪查一下就好, 就不在这介绍了 */ getName(); // 1 /* 这里为什么也是1 呢? 其实原因就是 上面我们调用了 `Foo().getName();` 这个方法引起的, 因为我们执行了 Foo 函数, 触发了 getName = function(){ console.log("1"); } 这段代码, 而且并没有在Foo里面声明 getName 变量, 于是就一直往上查找, 找到外部的 getName 变量 并赋值给它. 所以这里调用 getName() 方法时, 它的值已经变成 getName = function(){ console.log("1"); } 了 */ new Foo.getName(); // 2 /*这个时候还是没有实例化, 调用的还是它的静态方法*/ new Foo().getName(); // 3 /*因为实例化了,所以调的是原型上的方法*/
Ich erinnere mich, dass ich ein paar klassische Beispiele gesehen habe, aber nach langer Suche konnte ich sie nicht finden das ist alles für den Moment.
Gleiche Punkte:
Beide Operatoren sind zulässige Operanden von Jeder Typ, wenn die Operanden gleich sind, wird true zurückgegeben, andernfalls wird false zurückgegeben
Unterschied:
==: Der Operator ist Wird als Gleichheit bezeichnet und wird verwendet, um zu überprüfen, ob zwei Operanden gleich sind. Die Definition von Gleichheit ist hier sehr locker und ermöglicht die Typkonvertierung sind strikt gleich, es wird keine Typkonvertierung durchgeführt
d. Andernfalls wird false zurückgegeben
Wenn es sich um eine Zahl und die andere Partei um ein Objekt handelt, vergleichen Sie das Objekt mit valueOf() oder toString(), andernfalls wird es zurückgegeben return false
null, undefiniert wird nicht typkonvertiert, aber Sie sind beide gleich
// 不同类型,相同值 var a = 1 var b = '1' console.log(a == b) // => true console.log(a === b) // => false // 对象和字符串 console.log([1,2,3] == '1,2,3') // => true 因为 [1,2,3]调用了 toString()方法进行转换 // 对象和布尔 console.log([] == true) // => false []转换为字符串'',然后转换为数字0, true 转换成1 // 对象和数字 console.log(['1'] == 1) // => true []转换为字符串'1' console.log(2 == {valueOf: function(){return 2}}) // => true 调用了 valueOf()方法进行转换 // null, undefined 不会进行类型转换, 但它们俩相等 console.log(null == 1) // => false console.log(null == 0) // => false console.log(undefined == 1) // => false console.log(undefined == 0) // => false console.log(null == false) // => false console.log(undefined == false) // => false console.log(null == undefined) // => true console.log(null === undefined) // => false // NaN 跟任何东西都不相等(包括自己) console.log(NaN == NaN) // => false console.log(NaN === NaN) // => false
===
3. toSting und valueOf
: Rückgabe eine Zeichenfolge, die dieses Objekt widerspiegelt: Gibt seinen entsprechenden Originalwert zurück
toString
valueOf
toString
var arr = [1,2,3] var obj = { a: 1, b: 2 } console.log(arr.toString()) // => 1,2,3 console.log(obj.toString()) // => [object Object] // 那我们修改一下它原型上的 toString 方法呢 Array.prototype.toString = function(){ return 123 } Object.prototype.toString = function(){ return 456 } console.log(arr.toString()) // => 123 console.log(obj.toString()) // => 456 // 我们看下其余类型转换出来的结果, 基本都是转换成了字符串 console.log((new Date).toString()) // => Mon Feb 05 2018 17:45:47 GMT+0800 (中国标准时间) console.log(/\d+/g.toString()) // => "/\d+/g" console.log((new RegExp('asdad', 'ig')).toString()) // => "/asdad/gi" console.log(true.toString()) // => "true" console.log(false.toString()) // => "false" console.log(function(){console.log(1)}.toString()) // => "function (){console.log(1)}" console.log(Math.random().toString()) // => "0.2609205380591437"
var arr = [1,2,3] var obj = { a: 1, b: 2 } console.log(arr.valueOf()) // => [1, 2, 3] console.log(obj.valueOf()) // => {a: 1, b: 2} // 证明valueOf返回的是自身的原始值 // 同样我们修改下 valueOf 方法 Array.prototype.valueOf = function(){ return 123 } Object.prototype.valueOf = function(){ return 456 } console.log(arr.valueOf()) // => 123 console.log(obj.valueOf()) // => 456 // valueOf转化出来的基本都是原始值,复杂数据类型Object返回都是本身,除了Date 返回的是时间戳 console.log((new Date).valueOf()) // => 1517824550394 //返回的并不是字符串的世界时间了,而是时间戳 console.log(/\d+/g.valueOf()) // => 456 当我们不设置时valueOf时,正常返回的正则表式本身:/\d+/g,只是我们设置了 Object.prototype.valueOf 所以返回的时:456 console.log(Math.valueOf()) // => 456 同上 console.log(function(){console.log(1)}.valueOf()) // => 456 同上
var a = { toString: function() { console.log('你调用了a的toString函数') return 8 } } console.log( ++a) // 你调用了a的toString函数 // 9 // 当你设置了 toString 方法, 没有设置 valueOf 方法时,会调用toString方法,无视valueOf方法
var a = { num: 10, toString: function() { console.log('你调用了a的toString函数') return 8 }, valueOf: function() { console.log('你调用了a的valueOf函数') return this.num } } console.log( ++a) // 你调用了a的valueOf函数 // 11 // 而当你两者都设置了的时候,会优先取valueOf方法, 不会执行toString方法
"||" Solange eine davon wahr ist, ist die Bedingung erfüllt
„&&“ Alle Bedingungen müssen wahr sein, um die zu erfüllen Zustand
var a = true,b = false, c = true, d = false var str = 'none' if (b || d || a) { str = '现在是 ||' } console.log(str) // => '现在是 ||' ,因为其中a为true所有满足条件 var str = 'none' if (b || d ) { str = '现在是 ||' } console.log(str) // => 'none' ,因为b,d都是false, 不满足条件 var str = 'none' if (a && c && d) { str = '现在是 &&' } console.log(str) // => 'none' ,因为d是false, 其中有一个false就不满足条件 var str = 'none' if (a && c) { str = '现在是 &&' } console.log(str) // => '现在是 &&' ,因为b,d都是true, 满足条件
2. Wenn „||“ falsch ist Das Ergebnis ist „||“ und der folgende Wert wird zurückgegeben
var a = true,b = false, c = true, d = false var str = 'none' if (b || d || a) { str = '现在是 ||' } console.log(str) // => '现在是 ||' ,因为其中a为true所有满足条件 var str = 'none' if (b || d ) { str = '现在是 ||' } console.log(str) // => 'none' ,因为b,d都是false, 不满足条件 var str = 'none' if (a && c && d) { str = '现在是 &&' } console.log(str) // => 'none' ,因为d是false, 其中有一个false就不满足条件 var str = 'none' if (a && c) { str = '现在是 &&' } console.log(str) // => '现在是 &&' ,因为b,d都是true, 满足条件
2. Solange „&&“ mit „true“ vorangestellt ist, egal ob „&&“ " folgt true oder false, das Ergebnis gibt den Wert nach "&&" zurück
var a = false, b = true console.log(a && b) // => false 只要“&&”前面是false,无论“&&”后面是true还是false,结果都将返“&&”前面的值 console.log(b && a) // => false 只要“&&”前面是true,无论“&&”后面是true还是false,结果都将返“&&”后面的值
var name = '小刚' var person = { name: '小明', fn: function() { console.log(this.name + '撸代码') } } person.fn() // => 小明撸代码 // 如何把它变成 “小刚撸代码” 呢? // 我们可以用 call/bind/apply 分别来实现 person.fn.call(window) // => 小刚撸代码 person.fn.apply(window) // => 小刚撸代码 person.fn.bind(window)() // => 小刚撸代码
Was ist also der Unterschied zwischen bind und call, apply Was?
Im Gegensatz zu call und apply wird bind nicht sofort nach dem Binden ausgeführt. Es wird nur dieser Punkt der Funktion ermittelt und dann die Funktion
var name = "小红" var obj = { name: '小明', fn: function(){ console.log('我是'+this.name) } } setTimeout(obj.fn, 1000) // => 我是小红 // 我们可以用bind方法打印出 "我是小明" setTimeout(obj.fn.bind(obj), 1000) // => 我是小明 // 这个地方就不能用 call 或 apply 了, 不然我们把函数刚一方去就执行了 // 注意: bind()函数是在 ECMA-262 第五版才被加入 // 所以 你想兼容低版本的话 ,得需要自己实现 bind 函数 Function.prototype.bind = function (oThis) { if (typeof this !== "function") { throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply( this instanceof fNOP && oThis ? this : oThis || window, aArgs.concat(Array.prototype.slice.call(arguments)) ); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; };
这三个东西牵涉到的可能就是我们最常见到的 “同步”、“异步”、“任务队列”、“事件循环” 这几个概念了
例:
var data; $.ajax({ ... success: function(data) { data = data } }) console.log(data)
当我们从服务器获取到数据的时候,为什么打印出来的是undefined ?
解决这个问题之前我们先来了解javascript的运行环境
JavaScript是单线程语言,JS中所有的任务可以分为两种:同步任务和异步任务。
同步任务:
意思是我必须做完第一件事,才能做第二件事,按照顺序一件一件往下执行(在主线程上)
异步任务:
假如我第一件事需要花费 10s, 但是我第二件事急着要做, 于是我们就把第一件事告诉主线程,然后主线程暂停先放到某个地方, 等把第二件事完成之后,再去那个地方执行第一件事,第一件事也就可以理解为异步任务
任务队列(task queue):
任务队列是干嘛的呢; 上面我们说了异步任务的情况, 我们把第一件放到某个地方, 那某个地方是什么地方呢,就是 “任务队列” 这个东西。里面乘放的是所有异步任务。
Event Loop(事件循环)
当主线程上面所有同步任务执行完之后,主线程就会向任务队列中读取异步任务(队列方法:先进先出)
而且是一直重复向任务队列中,即使没有任务。它也会一直去轮询。
只不过在任务列表里面没有任务的时候, 主线程只需要稍微过一遍就行, 一旦遇到任务队列里面有任务的时候,就会去执行它
也就是说在我们打开网页的时候,JS引擎会一直执行事件循环,直到网页关闭
如图所示
由此,上面为什么会产生 undefined的原因了, 因为ajax 是异步任务,而我们console.log(data)是同步任务,所以先执行的同步任务,才会去执行 ajax
说了这么多,我们来看下 为什么我们很需要 从 callback
=> promise
=> async/await
因为很多时候我们需要把一个异步任务的返回值,传递给下一个函数,而且有时候是连续的n个
callback
// 只有一个callback的时候 function fn(callback) { setTimeout(function(){ callback && callback() }, 1000) } fn(function(){ console.log(1) }) // 一旦我们多几个呢? function fn(a){ // 传入a 返回a1 function fn1(a1){ function fn2(a2){ function fn3(a3){ console.log(a3) .... } } } } // 当项目一复杂,这滋味。。。
Promise
什么是promise?
Promise是异步编程的一种解决方案,同时也是ES6的内置对象,它有三种状态:
Promise方法
基本用法
let promise = new Promise( (resolve, reject) => { setTimeout(function(){ resolve(1) }, 1000) }) promise.then( res => { console.log(res)// 一秒之后打印1 })
我们把上面的回调地狱转换下
const fn = a => { return Promise.resolve(a) } const fn1 = a => { return Promise.resolve(a) } const fn2 = a => { // return Promise.resolve(a) return new Promise( (resolve, reject) => { setTimeout(function(){ resolve(a) },1000) }) } const fn3 = a => { // return Promise.resolve(a) return new Promise( (resolve, reject) => { setTimeout(function(){ resolve(a) },1000) }) } fn(123) .then(fn1) .then(fn2) .then(fn3) .then( res => { console.log(res) // => 123 })
这样就简单明了多了, 我们就不需要一层一层嵌套callback了,可以通过链式调用来解决callback的问题
然而,仅仅这样还是觉得不够好
因为这种面条式调用还是让人很不爽,而且 then 方法里面虽然是按先后顺序来的,但是其本身还是异步的
看下面这段代码
const promise = new Promise( (resolve, reject) => { setTimeout(function(){ resolve(222) }, 1000) }) console.log(111) promise.then( res => { console.log(res) }) console.log(333)
打印结果依然还是 111 => 333 => 222, 并不是我们想象的 111 => 222 => 333
依然不适合单线程的思维模式。所以下一个解决方案 又出现了
Promise.prototype.then() 接收两个函数,一个是处理成功后的函数,一个是处理错误结果的函数。可以进行链式调用
Promise.prototype.catch() 捕获异步操作时出现的异常, 一般我们用来代替.then方法的第二个参数
Promise.resolve() 接受一个参数值,可以是普通的值, 会返回到对应的Promise的then方法上
Promise.reject() 接受一个参数值,可以是普通的值, 会返回到对应的Promise的catch方法上或着then方法的第二个参数上
Promise.all() 接收一个参数,它必须是可以迭代的,比如数组。通常用来处理一些并发的异步操作。成功调用后返回一个数组,数组的值是有序的,即按照传入参数的数组的值操作后返回的结果
Promise.race() 接收一个可以迭代的参数,比如数组。但是只要其中有一个执行了,就算执行完了,不管是成功还是失败。
pending: 进行中
resolved: 已完成
rejected:已失败
async/await
这是ES7的语法,当然,在现在这种工程化的时代,基本babel编译之后也都是能在项目中引用的
基本用法跟规则
async 表示这是一个async函数,
await只能用在这个函数里面。后面应该跟着是 Promise 对象, 不跟的话也没关系, 但是await就不会在这里等待了
await 表示在这里等待promise返回结果
例:
const fn = () => { return new Promise( (resolve, reject) => { setTimeout(function(){ resolve(222) }, 1000) }) } (async function(){ console.log(111) let data = await fn() console.log(data) console.log(333) })() // 是不是返回 111 => 222 => 333 了呢 // 我们来试下返回别的东西, 不返回 promise const fn = () => { return new Promise( (resolve, reject) => { setTimeout(function(){ resolve(222) }, 1000) }) } (async function(){ console.log(111) let data = await fn() console.log(data) console.log(333) })() // 打印结果: 111 => null => 333 => 222 // 当我们不是在await 关键字后面返回的不是 promise 对象时, 它就不会在原地等待 promise执行完再执行, 而是向正常的JS一样执行,把异步任务跳过去
await
关键字必须包裹在 async
函数里面,而且async
函数必须是它的父函数
const fn = () => { let promise = new Promise( (resolve, reject) => { setTimeout(function(){ resolve(222) }, 1000) }) } // 这样是不行的,会报错,因为的await关键字的父函数不是 async 函数 const grand = async () => { return function parent() { let data = await fn() } } // 这样才行,因为await 的父函数 是一个 async 函数 const grand = () => { return async function parent() { let data = await fn() } }
柯里化
函数柯里化就是对高阶函数的降阶处理。
柯里化简单的说,就是把 n 个参数的函数,变成只接受一个参数的 n 个函数function(arg1,arg2)
变成function(arg1)(arg2)
function(arg1,arg2,arg3)
变成function(arg1)(arg2)(arg3)
function(arg1,arg2,arg3,arg4)
变成function(arg1)(arg2)(arg3)(arg4)
柯里化有什么作用
例:
//求和 function add (a, b, c) { return a + b + c } add(1,2,3)
如果我只改变 c 的值,在求和add(1,2,4)
是不是得多出重新计算 a + b 的部分
我们是不是可以提前返回a+b的值, 然后只传入 c 的值进行计算就行了
修改一下方法
function add (a, b) { return function (c) { return a + b + c } } var sum = add(1, 2) sum(3) sum(4)
在此基础上我们在做下修改
function add (a) { return function (b) { return function (c) { return a + b + c } } }
这样我们是不是可以随时复用某个参数,并且控制在某个阶段提前返回
还有一个经典的例子
var addEvent = function(el, type, fn, capture) { if (window.addEventListener) { el.addEventListener(type, function(e) { fn.call(el, e); }, capture); } else if (window.attachEvent) { el.attachEvent("on" + type, function(e) { fn.call(el, e); }); } };
我们每次调用事件时,都需要判断兼容问题, 但我们运用柯里化的方式就只要判断一次就行了
var addEvent = (function(){ if (window.addEventListener) { return function(el, sType, fn, capture) { el.addEventListener(sType, function(e) { fn.call(el, e); }, (capture)); }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, function(e) { fn.call(el, e); }); }; } })();
还有一个作用就是延迟计算
小明每天都会花一部分钱吃饭
小明想知道它5天之后总共会花费多少钱
var total = 0 var fn = function(num) { total += num } fn(50) fn(70) fn(60) fn(100) fn(80)
这样我们便能算出它总共花了都少钱
但是小明又突然想知道 如果他每天花费的的钱翻一倍 会产生多少钱
于是我们是不是得改下 上面的 函数
var fn = function(num) { total += num*2 } fn(50) fn(70) fn(60) fn(100) fn(80)
那我们是不是有什么办法,先把这些数 存起来,到最后在进行计算
我们接着来封装
var curry = function(fn) { var args = [] return function() { if (arguments.length === 0) { return fn.apply(null, args) }else{ args = args.concat([].slice.call(arguments)) return curry.call(null, fn, args) } } } var curryFn = function() { var args = [].slice.call(arguments), total = 0 for (var i = 0; i < args.length; i++) { total += args[i] } return total } var fn = curry(curryFn) fn(50) fn(70) fn(60) fn(100) fn(80) fn() //不传参数的时候进行计算
这样我们只有最后的时候才进行计算。
而且只需要修改 curryFn 里面的计算方法就行
我们整理下上面的方法封装完整的柯里化函数
var curry = function (fn, length) { length = length || fn.length; var sub_curry = function (f) { var args = [].slice.call(arguments, 1); return function () { return f.apply(null, args.concat([].slice.call(arguments))) } } return function () { var args = [].slice.call(arguments); if (length > args.length) { var newArgs = [fn].concat(args); return curry(sub_curry.apply(null,newArgs), length - args.length) }else{ fn.apply(null,arguments) } } }
// 1. var fn = curry( function(a,b,c){ console.log(a, b, c) }) fn('a')('b')('c') // 2. fn1 = curry(function(){ console.log(arguments) }, 3) fn1('a')('b')('c')
参数复用;
提前返回;
延迟计算/运行
反柯里化
反柯里化的作用在与扩大函数的适用性,使本来作为特定对象所拥有的功能的函数可以被任意对象所用.
被任意对象使用? 是不是想到了用call, apply 设置this指向
通过 call/apply 被任意对象所用
var obj = { a: 1, fn: function (b) { return this.a + b } } obj.fn(2) // 3 var obj1 = {a:4} obj.fn.call(obj1, 2) // 6
反柯里化版本
var uncurrying= function (fn) { return function () { var context=[].shift.call(arguments); return fn.apply(context,arguments); } } // const uncurrying = fn => (...args) => Function.prototype.call.apply(fn,args) // 简洁版 var f = function (b) { return this.a + b } var uncurry = uncurrying(f) var obj = {a:1}, obj1 = {a:4} uncurry(obj, 2) // 3 uncurry(obj1, 2) // 3
相信大家已经看出区别了,这丫的就相当于一个外部的call方法
上面很多只是自己的部分理解,不一定准确。如果有不同理解,谢谢指出。
相关推荐:
Das obige ist der detaillierte Inhalt vonZusammenfassung der JavaScript-Kenntnisse. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!