NODE基础概念
- node是工具或者环境
- 基于V8引擎(webkit)渲染和解析JS的
- 单线程
- 无阻塞I/O操作
- 事件驱动
- …
这里的单线程,无阻塞I/O操作,事件驱动,还要多理解
常见的DOS命令(mac大部分不支持,mac主要是linux命令)
Ping
测网速
Ping www.baidu.com
ipConfig
(mac不支持)
cls
清屏 (mac是clear)
dir
:查看当前目录下的所有文件(ls)
mkdir
创建文件夹
rm xxx.xx
删除文件
rmdir xxx
删除文件夹
NPM模块管理
npm网速慢的问题解决办法:
1.nrm来解决
npm install nrm -g
安装完成后,可以使用
-nrm ls 查看当先使用源
nrm use xxx 使用某个源 (如使用taobao)
2.yarn解决
首先安装yarn,安装到全局,然后基于yarn来安装我们需要的模块
npm install yarn -g
yarn add xxx
yarn remove xxx
基于yarn安装的,只能装到本地
JS一般放在BODY的尾部,为什么
SCRIPT标签中有defer和async两个属性,是用来干什么的
声明变量
var a = 12;
1.先声明一个变量a,没有赋值
2.在当前作用域中开辟一个位置存储12这个值
3.让a和12关联一起
var b = a;
把a存储到一个新的位置上,让新位置上的值和B保持关联,此时的B和A没有关系
上级作用域
当函数执行时,形成一个私有作用域A,A的上级作用域是谁,和他在哪执行的没有关系,和他在哪创建的有关,在哪创建,上级作用域就是谁。
arguments:实参集合
arguments.callee:当前函数本身
arguments.callee.caller:当前函数在哪执行的
function fn() {
console.log(arguments)
console.log(arguments.callee)
console.log(arguments.callee.caller)
}
在全局下,caller的结果是NULL
堆栈内存
堆内存:存储引用数据类型值(对象:键值对 函数:代码字符串)
栈内存:提供JS代码执行的环境(形成函数作用域)和存储基本类型值
【堆内存释放】
让所有引用堆内存空间地址的变量赋值为null即可(没有变量占用这个堆内存了,浏览器会在空闲的时候把它释放掉)
【栈内存释放】
一般情况下,当函数执行完成,所形成的私有作用域(栈内存)都会自动释放掉,在栈内存中存储的值也会释放掉,但是也有特殊情况
1.函数执行完成,当前形成的栈内存中,某些内容被栈内存以外的变量占用了,此时栈内存不能释放(一旦释放外面找不到原有的内容了)
2.全局栈内存只有在页面关闭的时候才会被释放掉
…
如果当前栈内存没有被释放,那么之前在栈内存中存储的基本值也不会被释放,能够一直保存下来
思考题:
var k = 1;
console.log(5+(++k)+(k++)+4+(k--)+(++k) + 3 + (--k) + (k++),k)
思考题2:
var i = 1;
function fn(i){
return function(n){
console.log(n+(++i))
}
}
var f = fn(2);
f(3);
fn(5)(6);
fn(7)(8);
f(4)
闭包中存储的值不销毁
function fn(){
var a = 1;
return function(){
a++;
console.log(a)
}
}
var f = fn()
f() // 2
f() // 3
思考题
1.session 和 cookie
2.
function foo(i) {
if (i < 0){
return;
}
console.log('begin:' + i);
foo(i - 1);
console.log('end:' + i);
}
foo(3);
结果为什么是
3.图片上传
闭包
函数执行形成一个私有的作用域,保护里面的私有变量不受外界的干扰,这种保护机制称之为“闭包”
市面上的开发者认为的闭包是:形成一个不销毁的私有作用域(柯里化函数,惰性函数)
柯里化函数
function fn(){
return funciton(){
}
}
var f = fn()
f()
闭包的保护作用:保护私有变量不受外界的干扰
闭包的保存作用:形成不销毁的栈内存,把一些值保存下来,方便后面的调取使用
闭包是如何防止变量定义重复的?在闭包中的写的方法,外面无法使用。
1.JQ这种方式,把需要暴露的方法抛到全局
(function(){
function jQuery(){
}
window.jQuery = window.$ = jQuery
})()
jQuery()
$()
2.Zepto基于return,把需要公用的方法暴露出去 推荐这种
var zepto = (function(){
return {
xxx:function(){
}
}
})()
Zepto.xxx();
This
1.给当前元素的某个事件绑定方法,当事件触发方法执行的时候,方法中的this是当前操作的元素对象
oBox.onclick=function(){
this:oBox
}
2.普通函数执行,函数中的this取决于执行的主体,谁执行的,this就是谁(执行主体:方法执行,看方法名前面是否有点,有的话就是点前面是谁this就是谁,不然this就是window)
var a = 1;
function fn(){
console.log(this.a)
}
var obj = {
a:123,
aa:fn
}
obj.fn() // 123
obj.aa.call(window) // 1
单例模式
作用:把描述同一件事物的属性和特征进行“分组,归类”(存储在同一个堆内存空间中),因此避免了全局变量之间的冲突和污染
单例设计模式命名的由来:
每一个命名空间都是JS中Object这个内置基类的实例,而实例之间是互相独立互不干扰的,所以我们称它为“单例:单独的实例”
高级单例模式
1.给命名空间赋值的时候,不是直接赋值一个对象,而是先执行匿名函数,形成一个私有作用域AA(不销毁的栈内存),在AA中创建一个堆内存,把堆内存地址赋值给命名空间
2.这种模式的好处,我们完全可以在AA中创造很多内容(变量OR函数),那些需要供外面调取使用的,我们暴露到返回的对象中(模块化实现的一种思想)
var nameSpace = (function(window){
var $={}
window.$ = $
$.test = function(a){
console.log(a)
}
return {
$
}
})(window);
箭头函数和普通函数,在obj中的箭头函数
var n = 2;
var obj = {
n:3,
fn:()=>{
return this.n
}
}
console.log(obj.fn()) // 2
var n = 2;
var obj = {
n:3,
fn:function(){
return this.n
}
}
console.log(obj.fn()) // 3
这个时候会直接执行函数吗?
var n = 2;
var obj = {
n:3,
fn:(function(n){
alert(n);
})(n)
}
var n = 2;
var obj = {
n:3,
fn:(function(n){
console.log(n)
})(this.n)
}
obj.fn() // 2
这时候打印出来为2,因为自执行函数执行的时候,堆内存还没有存储完键值对,所以obj.fn里面的n是window的n(2)
var n = 2;
var obj = {
n:3,
fn:function(n){
console.log(n)
}
}
obj.fn() // 3
非立即执行函数的时候就是3了
声明一个对象(*****)
var obj = {
a:1,
b:2
}
- var变量提升 var obj,fun
- 开辟一个堆内存,写key,value字符串,然后存到堆内存中
var n = 2;
var obj = {
n:3,
fn:(function(n){
n*=2;
this.n+=2;
var n=5;
return function(m){
this.n*=2;
console.log(m+(++n));
}
})(n)
}
var fn = obj.fn;
fn(3);
obj.fn(3);
console.log(n,obj.n)
单例模式来进行模块开发
var skipRender = (function(){
var fn = function(){
//...
}
//...
return {
init:function(){
},
fn:fn
}
})();
skipRender.init();
var weatherRender = (function(){
var fn = function(){
};
return {
init:function(){
fn(); // 调取自己模块中的
skipRender.fn(); // 调取别人模块中的方法
}
}
})()
weatherRender.init();
JS面向对象
类,实例,对象
js的内置类
- Number数字类 String Boolean Null Undefined Array RegExp Function Date…
- 0 NaN 1.3 都是数字类的实例
- HTMLCollection (每一个元素集合都是它的实例)
- NodeList
- EventTarget ->Node ->Element -> HTMLElement -> HTMLBodyElement ->
类,实例,对象
function Person(name){
this.name = name
}
var a = new Person('小明')
Person是类, a是Person的一个实例
js中创建值有两种方式
1.字面量表达式
2.构造函数模式
普通函数和构造函数执行差异
普通函数执行
开辟作用域(栈内存)
形参赋值
变量提升
代码自上而下执行
栈内存释放
构造函数执行
开辟作用域(栈内存)
形参赋值
变量提升
在堆内存中创建一个新对象,并且让函数中的执行主体(this)指向这个新的堆内存
代码自上而下执行
把对象返回给实例
栈内存释放
声明构造函数时,把构造函数中的内容当做字符串,存在堆内存中。
构造函数本身就有return了,如果在构造函数中再写return,有两种情况,第一种情况是如果返回引用类型,return的就是这个引用类型的东西,第二种情况是如果返回的是非引用类型的,即使返回了,也会被覆盖掉
function Fn(m){
this.m = m;
return 1
}
var a = new Fn(123)
a instanceof Fn // true
//结果:a{m:123}
function Fn(m){
this.m = m;
return {a:1}
}
var a = new Fn(123)
a instanceof Fn // false
//结果:a{a:1}
instanceof
检测某个实例是否隶属于某个类
1 instanceof Number // false
in
检测当前对象是否存在某个属性,但不区分私有属性还是公有属性
hasOwnProperty
检测当前属性是否为对象的私有属性
obj.hasOwnProperty(‘key’) // TRUE
in 结合 hasOwnProperty
编写方法hasPubProperty,检测当前属性是否为对象的公有属性,和hasOwnProperty对应
// obj:检测对象, attr:检测的属性
function hasPubProperty(obj, attr){
if((attr in obj) && !(obj.hasOwnProperty(attr))){
return true
}else{
return false
}
}
var a = new Number(1)
hasPubProperty(a,'toString') //true
prototype
1.所有的函数数据类型天生自带一个属性:prototype(原型),这个属性的值是一个对象,浏览器会默认给它开辟一个堆内存
2.开辟的堆内存中有一个天生自带的属性:constructor,这个属性存储的值是当前函数本身
3.每个对象都有一个__proto__的属性,这个属性指向当前实例所属类的prototype(如果不能确定它是谁的实例,都是Object的实例)
function Fn(name){
this.name = name
}
var a = new Fn('xiaoming')
a.__proto__ === Fn.prototype // true
每一个类都把供实例调取的公共属性方法,存储到自己的原型上,(prototype的作用是存储一些公用的属性和方法,供他的实例调取使用)
var a = [1,2,3]
a.__proto__.pop() // 执行了没反应
a.pop() // 删除了最后一个
原型链
基于__proto__向上查找的机制,当我们操作实例的某个属性或方法时,先找自己的空间中私有的属性或者方法
1.找到了,则结束查找
2.没找到,则基于__proto__找所属类的prototype,如果找到,就用这个公有的,如果没找到,则基于原型上的__proto__继续向上查找,一直找到Object.prototype的原型位置,如果都找不到,则操作的属性或方法不存在
面试题
console.log(a);
var a = 12;
function fn(){
console.log(a)
var a=13;
}
fn();
console.log(a);
结果:undefined undefined 12
console.log(a);
a = 12;
function fn(){
console.log(a)
a=13;
}
fn();
console.log(a);
结果:程序报错
3.这题要注意,不管条件是否成立,都会进行变量提升
var foo=1;
function bar(){
if(!foo){
var foo=10; // (**不管条件是否成立,都要进行变量提升**)
}
console.log(foo)
}
bar()
var n = 0;
function a(){
var n=10;
function b(){
n++;
console.log(n);
}
b()
return b;
}
var c = a();
c();
console.log(n)
// 11 12 0
- 写错了
// 形参和变量提升会放在私有作用域里面,和外界没有关系
var a=10,b=11,c=12;
function test(a){
a=1;
var b=2;
c=3;
}
test(10);
console.log(a);
console.log(b);
console.log(c);
// 我以为的结果: 1 11 3
// 实际的结果: 10 11 3
6.不会
/*
* 变量提升
* var a; 不管条件是否成立,都变量提升,window.a = undefined
* 所以a in window 是true
*/
if(!('a' in window)){
var a = 1;
}
console.log(a)
- 前两个值对了,最后一个值错了
var a = 4;
function b(x,y,a){
console.log(a);
arguments[2] = 10;
console.log(a);
}
a=b(1,2,3)
console.log(a)
// 结果
// 3 10 undefined
我本来以为a是function b(){}…,搞错了,因为这里是
a=b(1,2,3),
而不是a=b,
b(1,2,3),函数执行且没有返回值,所以是undefined
在非严格模式下,函数中的形参变量和ARGUMENTS存在映射机制(映射相互之间影响)
第一个形参变量修改为100,那么ARG[0]的值也跟着修改为100
ARG[1]的值修改为200,那么第二个形参变量Y的值也会跟着变为200
参考资料
https://www.cnblogs.com/wangtong111/p/11303191.html
function a(person){
console.log(person)
arguments[0] = 'person change'
console.log(person)
}
a('xiaoming')
// xiaoming
// person change
'use strict'
function a(person){
console.log(person)
arguments[0] = 'person change'
console.log(person)
}
a('xiaoming')
// xiaoming
// xiaoming
只传了一个参的情况
/*
* 形参
* x=0
* y=undefined
* ARG
* 0:10
* length:1(这时候长度为1就不会再改变了)
* ARG和形参是以ARG的索引为基础完成的,ARG中有这个索引,浏览器会完成和对应形参变量的映射,如果没有这个索引,那么多出来的形参是无法和ARG中对应的索引建立关联
*/
function fn(x,y){
var arg = arguments;
arg[0] = 100;
console.log(x);
y=200;
console.log(arg[1])
}
fn(10)
// 100
// undefined
fn(10,20)
// 100
// 200
function fn(x,y){
var arg = arguments;
arg[0] = 100;
arg[1] = 300
console.log(arguments)
console.log(y)
}
fn(10)
// 100 300
// undefined
arguments和形参的映射机制,建立在函数执行的一瞬间,如果当时没有关联的话,后面再修改也不会有影响。
- 面试题
var foo = 'hello';
(function(foo){
console.log(foo)
var foo = foo||'world'
console.log(foo)
})(foo)
console.log(foo)
//hello hello hello
1.新参赋值 foo = hello
2.变量提升 var foo;(这一步省略,因为在私有作用域已经有foo这个变量了)
- 面试题(对了)
var a=9;
function fn(){
a=0;
return function(b){
return b+a++;
}
}
var f = fn(); // 全局a=0;
console.log(f(5)); // 5+0; a++变1
console.log(fn()(5)); // a=0; 5+0; a++变1
console.log(f(5)); // 5+1 a++ => 2
console.log(a); // 2
10.面试题 (错了) ******
我的答案:
var ary = [1,2,3,4]
function fn(ary){
ary[0] = 0; // [0,2,3,4]
ary = [0]; // [0]
ary[0] = 100; // [100]
return ary; // [100]
}
var res = fn(ary);
console.log(ary) // [1,2,3,4]
console.log(res) // [100]
实际答案:
var ary = [1,2,3,4] //=> ary=bbbfff111 [ARY全局变量] =>[0,2,3,4]
function fn(ary){
/*
* 形参赋值:ary=bbbfff111 [ARY是私有变量]
*/
ary[0] = 0; // [0,2,3,4]
ary = [0]; // [0] 这时候开辟了一个新的空间 bbbfff222
ary[0] = 100; // [100] 这时候改的是bbbfff222,和bbbfff111没关系
return ary; // [100]
}
var res = fn(ary); //=> res=fn(bbbfff111)=bbbfff222
console.log(ary) // [0,2,3,4]
console.log(res) // [100]
[0,2,3,4]
[100]
11.面试题 对了
function fn(i){
// bbbfff000
return function(n){
console.log(n + (i++));
}
}
var f = fn(10); // f = bbbfff000 i=10
f(20); // n=20 => 20 + 10++ ; (30) i=11
fn(20)(40) // i=20 n=40 => 40+ 20++ (60)
fn(30)(50) // 50 + 30 ++ (80)
f(30) // n=30 => 30 + 11++; (41)
题目变换一下
var i = 10;
function fn(){
// bbbfff000
return function(n){
console.log(n + (++i));
}
}
var f = fn();
f(20) // n为20 i为10 20+ ++10=> (31) i=11
fn()(20) // n为20 i为11 20+ ++11 => (32) i=12
fn()(30) // 30 12 20+ ++12 => 43 i=13
f(30) // 44
私有变量和全局变量没有直接关系,但是如果全局变量赋值给私有变量是引用类型的话,修改私有变量也会改到全局变量
严格模式和非严格模式
1.函数形参映射机制,在严格模式下是没有这个机制的
2.在函数中打印arguments.callee 会报错
3.严格模式不能出现相同的形参
function test(a,a){
console.log(a)
}
test(1,2)
// 2
'use strict'
function test(a,a){
console.log(a)
}
test(1,2)
// 报错 Uncaught SyntaxError: Duplicate parameter name not allowed in this context
// 重复的参数名不能存在这个语境中
4.在严格模式下,函数执行,如果没有明确指定执行的主题(函数前面没有点),不再像非严格模式下一样,统一交给window,而是让this指向undefined,代表没有执行主体:“严格模式下,有执行主体this就指向执行主体,不然this就是undefined”
高程3,最后有严格模式和非严格模式的规则汇总
&&的用法
常用在回调函数,如果有回调的话,执行回调
function fn(callback){
//if(typeof callback==="function"){
// callback()
//}
callback && callback() // 和上面的一样
}
&&和||
&&优先级大于||
0||1&&2
// 如果前面成立则值就是前面
1||4
5||1
// 如果前面成立则值是后面
5&&4
优先级参考网址
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
面题
0 || 1 && 2 || 0 || 3 && 2 || 1
注意点
function a(x=1){
console.log(x)
}
a(undefined)
这时候浏览器会按照没有传递值处理
函数return一个引用类型值,执行的作用域不销毁
ES6解构参数 (还没有很懂)
var a = ({x:x})=>{
console.log(x)
}
a({x:1})
// 1
函数体内的this是定义时所在的对象而不是使用时所在的对象
var num = 11
function a(){
console.log(this.num)
}
var b = {
num:123,
c:function(){
a()
}
}
// 11
参数映射,如果传入的是引用类型的值,修改函数参数时,原来的值也会改变
function a(num){
num[0] = 2
console.log(num)
}
var obj = {b:[1,2,3]}
a(obj.b)
console.log(obj.b)
// [2,2,3]
每一个类都有一个prototype属性,属性值是一个对象,这个对象中存储了提供实例使用的公用方法和属性
在浏览器默认给原型开辟的堆内存中有一个constructor属性,这个constructor指向构造函数本身
每个对象实例都有__proto__(原型链)属性,这个属性指向当前实例所属类的prototype
new 一个对象后发生了什么
1.参数赋值以及创造函数执行的作用域名
2.创建实例对象,并且把this指向实例
3.执行代码
4.把创建的实例对象返回
this面试题
元素绑定对象,this是当前操作的元素
方法名前面是否有带你,有点,点前面是谁this就是谁,没有this就是window,严格模式下是undefined
构造函数执行,方法中的this是当前类的一个实例
var obj = {
fullName:'js',
prop:{
getFullName: function(){
return this.fullName
}
}
}
console.log(obj.prop.getFullName()) // this:obj.prop =>obj.prop.fullname => undefined
var test = obj.prop.getFullName;
console.log(test()) // 函数执行前面没有点,所以是window,window.fullName
原型面试题 (写错了)
修改构造函数的prototype属性,会导致构造函数的prototype指向一个新的堆内存,原来的prototype就被替代了,当浏览器空闲的时候,会把这个原来prototype所占用的堆内存给释放掉。新开辟的堆内存有一个问题,就是他没有constructor属性
function fun(){
this.a =0;
this.b=function(){
alert(this.a)
}
}
fun.prototype = {
b:function(){
this.a=20;
alert(this.a)
},
c:function(){
this.a=30;
alert(this.a)
}
}
var my_fun = new fun();
my_fun.b();
my_fun.c()
es6 继承
class Animal{
constructor(name){
this.name = name
}
bark(){
console.log(this.name+'叫:XXX')
}
eat(){
console.log(this.name+'叫:喵喵喵')
}
}
class Cat extends Animal{
constructor(name){
super(name)
this.name = name
}
bark(){
console.log(this.name+'喵喵喵')
}
}
let dog = new Animal('旺财')
dog.bark()
let maomi = new Cat('咪咪')
maomi.bark()
原型面试题
var n = 10;
this.m = 20;
this.aa = function(){
console.log(this.m)
}
}
Fn.prototype.bb = function(){
console.log(this.n)
}
var f1 = new Fn;
Fn.prototype={
aa:function(){
console.log(this.m+10)
}
}
var f2 = new Fn;
console.log(f1.constructor)
console.log(f2.constructor)
数组去重,基于es5,(es6用Set)
function uniqueArr(arr){
var obj = {}
for(var i=0;i<arr.length;i++){
var key = arr[i]
if(obj[key]){
console.log('有' + key + '了')
continue
}else{
obj[key] = 1
}
}
return Object.keys(obj)
}