JS正式课第一周笔记整理
webstorm :
代码编辑器
浏览器:
代码解析器;Git :
是一个工具;用于团队协作开发项目管理代码的工具;在工作中用git、svn
svn :
集中式;
集中式:
需要一个*服务器;每次开发前需要从*服务器把最新的代码拉取下来,然后进行开发;并且需要网络;
git :
分布式;
分布式:
每个人的电脑都是一个*服务器;不需要网络也可以进行提交代码;
DOS命令
- 1.按着shift右键,在此处打开powerShell
- 2.找到相应的文件夹,直接输入cmd,按回车;
- window+r : 输入cmd
切换磁盘路径
- cd + 文件夹名字: 打开相应的路径
- cd ../ : 回到上一级
- cd / : 回到跟路径;
- mkdir + 文件名字 : 创建文件夹
- cls : 清除所有的命令
Git
配置Git用户名和密码
- git config --global user.email "邮箱"
- git config --global user.name "用户名"
Git的简单命令
- git init : 初始化一个git 仓库(生产.git文件)
-
git add .
: 把所有文件提交到暂存区 - git add [文件名]:推送指定的文件到暂存区
-
git commit -m"注释" : 把代码提交到历史区;(
推送到历史区之后Git会自动分配一个版本号xxxxxx
,根据版本号可以回到任何一次修改的位置) - git status:查看文件状态;如果文件只存在本地,不在暂存区,那么颜色是
红色
,如果文件在暂存区还没有提交到历史区,那么颜色是绿色
的 本地代码- ->暂存区- ->历史区
本地仓库与远程仓库通信
- git remote add origin(仓库名) url地址:将本地仓库与远程仓库建立连接
- git remote -v:查看本地仓库和远程仓库关联状态
-
git push origin(
与建立连接时的仓库名一样
) master(主分支名
):将历史区内容推送到远程仓库 - git config --list:查看本地配置环境
- 输入用户名和密码
推送过程(详细步骤)
本地操作
- ① git init(初始化git仓库)
- ② git add .(将文件推送到暂存区)
- ③ git status(查看文件状态)
- ④ git commit -m "注释"(将代码提交到历史区,并添加注释)
远程仓库操作
- git remote add origin(仓库名) url地址
- git remote -v
-
git push origin(仓库名) master
推送复原过程
拉取克隆过程(详细步骤)
- git clone 仓库地址url:这里已经是一个git仓库,并且已经和远程建立和连接
-
git pull origin master:拉取远程仓库代码
GitHub
-
GitHib
:是一个开源网站,可以供大家分享代码 插件和框架
- 把本地的代码提交到远程仓库(GitHub)
- 让本地仓库和远程仓库进行绑定
作用域
作用域:
代码执行的空间环境 ===栈内存
浏览器的渲染机制:
先形成一个DOM树和CSS树,最后两个树合成render树
全局作用域 :
当打开浏览器时,浏览器会形成一个全局的作用域,给当前代码提供运行环境的;并且存储基本数据类型值;
- 存储基本数据类型值
- 基本数据类型存储到栈内存中,全局作用域是最大的一个栈内存
- window是全局中最大的一个对象,存储了大量的内置属性
私有作用域:
一个函数执行的时候就会形成一个私有作用域,函数执行完成(或关闭浏览器)就销毁块级作用域:
ES6新增的,除了私有作用域和对象以外所有被一个{}包裹起来的,(三个判断,三个循环)eval("({a:1})")、for 循环中用let ,会形成父作用域;每循环一次,都会形成一个子作用域;在子作用域中会把当前的i进行保存
全局变量:在全局作用域下定义的变量
- 会给window新增一个键值对
- 全局下定义的函数相当于给window新增键值对,属性名是函数,属性值是整个函数
私有变量:
- 在函数体中被var 、function、const、let声明
- 函数的形参也是私有变量
性能优化
//1-
function fn(){
//传入任意参数求和
var total = 0;
var len = arguments.length;
//(var i = 0;i<arguments.lenth;i++)
for(var i = 0;i<len;i++){
var cur = arguments[i];
if(!isNaN(cur)){
//total += arguments[i];
total += cur;
}
}
}
//2- in 方法用于检测对象中是否有该属性
function fn(){}
console.log('fn' in window);//true
//'fn'必须加引号,不叫引号就是这个变量对应的值
//3-
var a,b,fn = function(){
var a = b = 10;
//(var a = b = 10;-->var a = 10;b = 10;)
//a变成了局部变量无法修改外部,b是全局变量,修改了外部
}
fn();
console.log(a,b);//undefined 10
变量提升
https://blog.csdn.net/zjy_android_blog/article/details/80863425
https://blog.csdn.net/longwenjunjie/article/details/48163293
变量提升的定义
变量提升:
在作用域形成之后代码执行之前,将所有带var和function关键字的变量提前声明或定义JS可以在浏览器中农运行 是因为浏览器给JS提供了赖以生存的环境,这个环境就是作用域
- var:只是提前声明;默认值是undefined
- function:既声明又定义
- debugger:打断点
形成作用域-变量提升-代码从上到下执行
变量提升的特殊情况
- 不管条件是否成立,都要进行变量提升
- 条件判断中的函数,在条件判断之前,带
var
和带function
都只是声明不定义
- 当条件成立之后,第一件事就是给函数复制;
- 条件不成立,判断体就不执行,结合ES6的
块级作用域
- 如果不带
var
直接赋值就相当于给window直接添加了一个属性 并且给它赋值 - 带
var
就相当于子安全局作用域下增加了一个属性,还给全局对象window增加了一个属性 -
注意:
带var
的可以不赋值,但是也相当于给全局对象window增加了一个属性.不带var
的必须要赋值,否则就会去找全局对象window上是否有这个属性,没有这个属性就会报错.
- 用逗号分隔的表示连
var
;如:var a = 1,b = 2,c = 3;- 用分号(分号代表代码段)来分隔的,前面带
var
的属于变量,不带var
的相当于给全局对象window添加了属性;如:var a = 1;b = 2;c = 3;- 连等 带
var
的是一个变量,其他的不带var
的就相当于给全局对象window添加了属性
- 变量提升只发生在
=
左边 - return后面的内容不进行变量提升 但是下面的 代码要进行变量提升
function f3(){
console.log(f4());//打印f3函数体中的内容
console.log(f2());//报错f2没有进行变量提升
return function f2(){
//return 后面的内容不进行变量提升 但是下面的代码要进行变量提升
}
function f4(){
console.log(f3);
}
}
f3();
var a = [1];
b = a;
b[1] = 'a';//b = [1,'a']
console.log(a);//[1,'a'] b指向了a的地址,修改的时候修改了地址中的属性以及属性值
- 变量名重复:不再进行声明,但是要重新定义 (函数名跟变量名重复取函数名、函数名和函数名重复会覆盖)
- 函数当做参数的时候,不进行变量提升
- 匿名函数不进行变量提升
- 自执行函数不会进行变量提升
- let const声明的变量不进行变量提升
函数的定义与执行
- 定义的三步曲:
- 开辟新的堆内存
- 存储代码字符串
- 返回内存空间地址
- 函数执行过程:
- 首先会形成一个私有作用域,开辟一个栈内存
- 形参赋值
- 变量提升
- 代码从上到下执行
- 作用域是否销毁
- 对象的定义:
- 开辟一个空间地址,堆内存
- 把键值对存储到堆内存下
- 把空间地址赋值给对象名
console.log(fn);//fn()
console.log(f);//undefined
function fn(){}//函数声明和定义
var f = function(){}//函数表达式-->箭头函数
console.log(fn);//fn函数体
console.log(f);//undefined
console.log(fn());//打印两次undefined 函数执行的时候打印f为undefined,并且函数没有返回值 打印一次undefined
function fn(){
console.log(f);
}
var f = function(){}//函数声明
console.log(f2);//f2未定义 报错
var
var f = function f2(){}
console.log(f2)//f2未定义 报错
setTimeout(funtion f2(){},1000)//函数作为参数的时候不进行变量提升
函数声明提升
1、函数的两种创建方式
- 函数声明
- 函数表达式
函数声明的语法
f('nihao');
function f(name){
console.log(name);
}
//能打印出nihao
函数表达式语法
f('nihao');
var f = function (name){
console.log(name);
}
//控制台报错 Uncaught ReferenceError:f id not function错误显示f不是一个函数
两种方式的区别:
- 函数声明又一个非常重要的特征:
函数声明提升
,函数声明语句将会被外部脚本或者外部函数作用域的顶部(跟变量提升非长相似)。正是这个特征,所以可以把函数声明放在调用它的语句后面。
var getName = function (){
console.log(2);
}
function getName(){
console.log(1);
}
getName();
//打印出2
这个例子涉及到了变量声明提升
和函数声明提升
.正如前面提及到的函数声明提升,函数声明function getName(){}
的声明会被提前到顶部.而函数表达式var getName = function(){}
则表现出变量声明提升.因此,在这种情况下,getName也是一个变量,这个变量的声明将被提升到底部,而变量的赋值依然保留在原来的位置.需要注意的是:
函数优先,虽然函数声明和变量声明都会被提升,但是函数会首先被提升,然后才是变量。因此上面的函数可以换成这个样子
function getName(){
//函数声明提升到顶部
console.log(1);
}
var getName;//变量声明提升
getName = function(){
//变量赋值依然保留在原来的位置
console.log(2);
}
getName();//最终输出2
在原来的例子中,函数声明虽然是在函数表达式之后,但是由于函数声明提升到顶部,因此后面getName
又被函数表达式的赋值操作给覆盖了,所以控制台输出了2
console.log(f);//打印函数体
function f(){
console.log(1);
}
f();//88
function f(){
console.log(2);
}
function f(){
console.log(3);
}
f();//88
f = 99;
function f(){
console.log(88);
}
f();//f is not a function
console.log(f);
ES6的let和const
var 与ES6中const 、let声明的变量的区别
https://blog.csdn.net/qq_22855325/article/details/72843456
let
不能和函数重名
-
const
声明的变量,
- 如果是基本数据类型,那么不可以进行数据修改.
- 如果 是引用数据类型值,就可以操作引用地址,不可以替换引用地址
- 没有变量提升
- 不可以重复声明
- 定义的变量不会给window增加属性
- 定义的是个常量,定义之后不可以修改
- 一旦声明必须赋值
-
let
虽然不会进行变量提升,但是会先检查当前作用域下是否有重复命名
- 没有变量提升
- 不可以重复声明
- 定义的变量不会给window增加属性
var a = 2;
if('a' in window){
console.log(a);//形成暂时性死区,即在块级作用域下,不能提前拿到let声明的变量的值,打印2
let a = 1;
}
let
ES6中提出了一个新的变量,不在于取代var
,而在于解决ES5中var
声明中的一些痛点;这就是let
let的特点
- 1、
let
是块级变量,不存在于window下[非全局属性],window,变量名
是找不到的,它的作用范围就那么一小块 - 2、
let
不允许重新声明同名变量,会抛出异常,具有唯一性 - 3、
let
不允许没声明就使用,会抛出异常,只有执行该声明的时候才能使用 - 4、
let
有自己特色的闭包特性,比如在for
循环的应用中
//1-
let tt = 'nihao';
console.log(tt);//打印出'nihao'
console.log(window.tt);//打印出undefined
//2-
function test2(){
var abc = 99;
let abc = 88;
console.log(abc);
}
test2();//打印值:Uncaught SyntaxError:Indentifier 'abc' has already been declared
//3-
function test3(){
console.log(test3);
let test3 = '哈哈哈哈';
}
test3();//打印值:Uncaught ReferenceError:test3 is not defined
//4-每一次for村换都重新绑定一次作用域且脱离失效,就是let自身的特色
for(let i = 0;i<9;i++){
//for循环形成保护机制
console.log('循环内的值是:'+i);
}
console.log(i);//打印值
//循环内的值是:0
//循环内的值是:1
//循环内的值是:2
//循环内的值是:3
//循环内的值是:4
//循环内的值是:5
//循环内的值是:6
//循环内的值是:7
//循环内的值是:8
//Uncaught ReferenceError:i is not defined 块级作用域 外界无法访问
查找上一级的作用域
1、在当前作用域下输出变量值时,首先观察是否是私有变量
- 如何判断一个变量是私有的
- 当前变量有没有被
var
过和function
过 - 形参也是私有变量
2、如果变量不是私有的,就向上一级作用域查找
- 上一级作用域判断函数在哪定义的,函数上一级的作用域就是谁,跟函数执行没有任何关系
- 但获取变量值时,首先是否是私有变量,如果不是就向上一级作用域查找,一级一级向上,直到
window
为止,如果window
也没有,那么就会报错,这样一级一级向上查找作用域就是作用域链
堆内存的销毁
形成两个虚拟内存:栈内存、堆内存
栈内存:作用域
引用数据类型
在JS中的{}[]都是开辟一个新的空间地址谷歌浏览器:
每隔一段时间,自动检查占用并是否销毁火狐和IE:
采用的计数的规则,当堆内存地址被占用一次时,计数+1,否则-1,如果是被占用0次,就回收
私有作用域的销毁
-
函数的执行:
形成一个私有的作用域,提供代码运行环境,存储基本数据类型
闭包
- 保护里面的私有变量不受外界干扰
- 存储值
- 保护机制:当前私有作用域中,有引用数据类型被外界所占有,导致当前作用域变成一个不销毁的作用域,里面的变量就成了不销毁的变量
一般情况下,函数执行完成之后,当前作用域就会销毁
函数没执行一次就会开辟一个新的私有作用域,并且新的私有作用域和之前的作用域没有任何关系,是两个不同的栈内存
不销毁的作用域:
- 函数执行return出一个引用的数据类型值
- 函数执行return出的引用数据类型值并且被外界接收(被占用)
栈内存
浏览器加载的时候,碰到引用数据类型,都会开辟一个新的内存空间,[对象:键值对;函数:代码字符串],给这个内存空间赋一个16进制内存地址,将这个内存地址指向声明的变量,这个内存空间就是堆内存
堆内存的释放,手动赋值null,[指向空指针];浏览器判断该内存没有变量就去收回它,就会释放
function fn(){
var t = 10;
return function (){
console.log(t++)
}
}
var f = fn();
f();
f();
不立即销毁:
- 需要等到里面的小函数执行完成之后,那么外层作用域就会销毁
function fn(){
var t = 10;
return function (){
console.log(t++);
}
}
fn()();
var i = 0;
function fn(){
//i = 5 6;
return function(n){
console.log(n + i++);
}
}
var f = fn(5);
f(10);//15
f(20);//26
fn(8)(12);//20
fn()(18);//NaN
function fn (){
var a = 1 ;
return function (){
a++;
console.log(a);
}
}
var f = fn();
f(); //2
fn()(); //2
f(); //3
var obj= {
i : 10,
fn:(function () {
var i = 0;
return function (n) {
console.log(n + i++)
}
})()
};
var f = obj.fn;
f(10);// 10
f(20);// 21
obj.fn(30);// 32 块级作用域
obj.fn(40);// 43
this关键字
- 函数中的
this
,指的就是函数的当前执行主体 -
1、在全局作用域下,
this
指向window
-
2、函数体中的
this
,看函数执行前有没有'.'
;如果有,那么'.'
前面是谁,this
就指向谁;如果没有'.'
,那么会指向window -
3、如果给元素的事件行为绑定方法,,那么方法中的
this
,就会指向当前被绑定的那个元素 -
4、回调函数中的
this
指向window
-
5、自执行函数中的
this
一般都指向window
-
6、
forEach
、map
第二个参数可以修改回调函数中的this
-
7、构造函数中的
this
指向当前类的实例 - 8、call、apply、bind可以改变this关键字的指向
-
this
是谁,和它在哪定义的以及在哪执行的没有任何关系
function b() {
console.log(this); //window
}
window.b();
var obj = {
num:1,
fn : function () {
console.log(this);
function m() {
console.log(this);// window;
}
m()
}
}
var f = obj.fn;
f(); // window
obj.fn();// obj
var obj = {
num:1,
f:{
num:2,
fn:function () {
console.log(this);// obj.f
}
}
}
obj.f.fn()
//1.
setInterval(function () {
console.log(this); // window
},1000)
//2.
(function () {
console.log(this); //window
})()
//3.
var obj = {
fn: (function () {
console.log(this); //window
})()
}
var num =2;// 1 2
var obj = {
num : 0,
fn : function () {
num = 1;
// this-->obj
(function (num) {
// this --> window
++this.num;
num++;
console.log(num)// 1
})(this.num)// 0
}
}
obj.fn();
console.log(window.num,obj.num) //2 0
this的重点练习题
var num =2;// 1 2
var obj = {
num : 0,
fn : function () {
num = 1;
// this-->obj
(function (num) {
// this --> window
++this.num;
num++;
console.log(num)// 1
})(this.num)// 0
}
}
obj.fn();
console.log(window.num,obj.num) //2 0
设计模式-单例模式
模块化开发
把一个项目按照页面或者功能分割成不同的任务,交给项目中不同的成员开发.开发结束之后
将代码合并到一起.
- 多人协作开发的问题: 变量名 函数名 冲突
- 为了防止全局变量污染: 闭包但是闭包并不完美,所以选择搭配设计模式来进一步进行
项目开发
单例模式
表现形式:就是一个对象:{name:xxx,age:function(){}};
-
定义:把描述同一件事物的不同属性放在同一个对象
[空间]
下,避免了全局变量的干扰;这种模式就是单例模式 - 在设计模式当中,这个
person
不仅仅是一个对象名,还是一个命名空间,多个命名空间是相互独立的,互不干扰 - 好处:不受全局变量的污染和干干扰,并且可以相互调用方法
- 由来:单例模式的本质就是一个对象,它是object类上的一个实例,实例与实例之间互不干扰,叫做单独的实例,简称"单例"
高级单例模式
- 定义:在高级单例模式当中,我们不只会把一个对象赋值给一个命名空间,我们会先让它执行一个自执行函数,[就形成了一个闭包,一个不销毁的作用域],在自执行函数中,返回一个对象给这个命名空间
-
好处:可以在私有的作用域当中,创建很多的属性和方法,仅需要把我们使用的属性和方法暴露在这个对象当中即可。
相对于单例模式,可以存储不同方法中的公有变量
工厂模式
- 定义:把实现同一种功能的代码放进一个函数中,当想实现类似功能时,只需要执行这个函数即可,传参数不同就可以.
- 好处:减少了代码的冗余:"高内聚、低耦合"-->函数的封装
面向对象
-
OOP/OP
:面向对象的缩写,面向对象思想 -
定义:
面向对象的封装、继承和多态,通过简单的实例化,调用其他的方法和属性 -
JS
就是通过面向对象的思想设计开发出来的 - 需要了解 对象、类、实例
-
对象:
万物皆对象,多为一种泛指,可以是任何物体 -
类:
对象中具有同一属性和特征的集合,类又分为大类和小类 -
实例:
就是类中的一个具体的细分,我们研究当前实例具有哪些属性和方法,那么证明当前类中的其他实例也同样具有
JS中的内置类
- JS中的任何数据类型都是它所属的类,除了null、undefined
- Number、String、Boolean、Object、Array RegExp、Function、Date...
- 类的首字母都是大写
- 类的执行通过new来执行
- Object对象类,被称为基类,在任何数据类型都可以通过__proto__[原型链]找到基类Object
创建类
- 字面量方式
- 实例化方式
- 引用数据类型方式创建 var ary = new Array('1')
注意:
- new Array(10):创建一个长度为10的数组,数组中的每一项都是空
- new Array('10'):如果只传递一个实参,并且实参不是数字,相当于把当前值作为数组的第一项存储进来
- new Array(10,20,30):如果传递多个实参,不是设置长度,而是把传递的内容当做数组中的每一项存储起来
构造函数
-
定义:当一个函数通过
new
关键字来执行的时候,这个函数就不是普通函数了,它是一个构造函数,也是一个自定义类,当前的函数名就是类名,这个函数的返回值就是这个类的实例 - 为了让构造函数和普通函数有一个区别,我们建议写构造函数的时候首字母大写
[类名]
- 注意:一般情况下,我们写业务逻辑的时候不会用到构造函数,但是在封装库、插件以及组件的时候就会用到构造函数模式
- 构造函数执行的时候,如果没有参数的时候,小括号可以不写
let person = new Person
普通函数和构造函数的区别
- 相同点:形成私有的作用域-->形参赋值-->变量提升-->从上到下执行-->作用域销毁
-
不同点:
- 构造函数运行时,
形成作用域后,在代码运行之前,
首先会给当前的作用域初始化一个空对象;并且让当前作用域下的this
指向这个空对象 当代码运行结束,构造函数把函数体中的this作为返回值返回 - 构造函数如果并不需要传参,可以省略执行的小括号
- 构造函数中的
this
,指向当前的实例 - 在构造函数中,
return
一个基本数据类型值,那么对实例没有任何影响;如果return
出一个引用数据类型值,那么会把默认return
的this
替换掉.
- 构造函数运行时,
构造函数的执行过程
- 形成一个私有的作用域
- 形参赋值
- 变量提升
- 浏览器会创建一个对象,[开辟一个新的堆内存],将这个对象指向了
this
[堆内存指针指向this
实例] - 代码从上到下执行
- 判断当前构造函数是否有
return
,
- 如果没有
return
默认将实例返回; - 如果有
return
,- 如果return的是基本数据类型,对实例没有影响
- 如果是引用数据类型,那么实例就是该引用数据类型
- 构造函数中:建议不要轻易
return
引用数据类型
私有属性
- 在构造函数中,给this添加属性值和方法,都属于当前实例的私有属性
公有属性
- 当前实例通过__proto__找到所有的属性和方法都输属于当前实例的公有属性
- 实例想要调取公有属性,直接可以调取,底层及时通过__proto__去找这个属性
- 用in这种方式来判断,当前属性名
[公有属性+私有属性]
是都属于这个对象console.log('hasOwnProperty' in person1)
; - Object类提供一个
hasOwnProperty
,这个方法判断当前属性是否是该实例的私有属性:返回布尔值console.log(person1.hasOwnProperty("age")) //true
console.log(person1.hasOwnProperty("valueof")) //false
例题:自己封装一个方法,判断当前属性是否是当前实例的一个公有属性hasPubProperty
function hasPubProperty(obj,item){
//先检测是否是属性
return item in obj &&!obj.hasOwnProperty(item);
}
console.log(hasPubProperty([], 'toString'));
JS中的函数
- 普通函数、类(自定义类和内置类)、函数类的实例
对象
- 普通对象(对象数据类型)
- 构造函数
new
出来的一个实例,也是一个对象 - 类上面的原型也是一个对象
- 函数也是一个对象
学习原型模式需要记住三句话
所有的函数都天生自带一个属性,叫做
prototype(原型)
,它是一个对象,既然是对象,那就是一个堆内存所有函数的原型上面(都是开辟的这个堆内存),都天生自带一个属性,叫做
constructor(构造函数)
,它指向当前类本身所有的对象都天生自带一个属性__proto__,它指向当前类的原型
所有的函数数据类型(普通函数、类(内置的、自定义))都是Function的一个实例;Function是所有函数的基类;
5.所有的对象数据类型(实例、prototype、对象)都是Object的一个实例;Object是所有对象数据类型的基类;
Function 首先是自己的一个实例;
function People(name){
let age = 9;
this.name = name;
this.age = age;
}
People.prototype.say = function () {
console.log(this);
};
let person1 = new People('zf');
let person2 = new People('zhufeng');
//person1属于People类
//__proto__指向类People的原型prototype
console.log(person1.__proto__ == People.prototype);//true
原型链
- 定义:一个实例要找属性,优先会去找自己的私有属性,如果自己的私有属性没有,那就通过__proto__找到自己所属类的原型上面的公有属性,如果公有属性还没有,继续通过__proto__找到自己的所属类的原型直到Object[基类]的原型上,一直找到基类还没有的话,直接返回undefined
Object类和Function类
Function类:
- 所有的类都是通过函数的方式来创建,由此可以得知,所有的类(包括基类Object)都是函数类的一个实例
- 判断一个实例是否属于一个类:intanceof
console.log(Number instanceof Function); //true
console.log(String instanceof Function); //true
console.log(Object instanceof Function); //true
- constructor:通过实例来调用这个方法,指向当前类本身
- 万物皆对象,JS中的任何数据类型都可以通过proto