珠峰笔记(变量提升-闭包-this-OOP)第三周

NODE基础概念

  1. 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
}

  1. var变量提升 var obj,fun
  2. 开辟一个堆内存,写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
  1. 写错了
// 形参和变量提升会放在私有作用域里面,和外界没有关系
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)
  1. 前两个值对了,最后一个值错了
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和形参的映射机制,建立在函数执行的一瞬间,如果当时没有关联的话,后面再修改也不会有影响。

  1. 面试题
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这个变量了)

  1. 面试题(对了)
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)
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值