JavaScript 学习笔记

五种基本数据类型和一种复杂数据类型

基本数据类型5种
 undefined 
 null 
 boolean 
 number    不区分浮点数和整数, 一律使用number来表示
 string

复杂数据类型1种
Object类型, 也是所有其他对象的基类

最基础的语法:

编程语言基础语法差异不大, 无非是数据类型, 操作符, 控制语句, 函数等

基本操作符:

常用的操作股包括:算术操作符 , 关系操作符, 布尔操作符, 赋值操作符

控制语句

if语句
switch语句
for语句
while语句
for-in语句

函数

函数是一小段逻辑的封装, 理论上逻辑越独立越好.
JavaScript函数相对其他语言来说有很大不同. JavaScript函数既可以作为参数, 也可以作为返回值.
此外JavaScript函数可以接受任意数量的参数, 并且可以通过arguments对象访问这些参数.
任何一门语言的基础语法都是相通的, 除开一些细节差异, 大致就是上面这些了: 数据类型,操作符, 控制语句, 函数, 模块等等

复杂概念,进阶
变量,作用域,内存问题
1.变量

JavaScript变量分为两种: 基本类型和引用类型.其中基本类型就是前面的五种基本数据类型 引用类型就是前面的Object以及基于它的复杂数据类型.

基本数据类型

在内存中占据实际的大小控件, 赋值的时候, 会在内存中穿件一份新的副本.保存在"栈内存"中

引用类型:

只想对象的指针而不是对象本身, 赋值的时候 只是其创建了一个新的指针指向对象.保存在"堆内存"中.

简单来说就是:基本类型在内存中是实际的值;而引用类型在内存汇总就是一个指针,指向一个对象, 多个引用类型可能同时指向同一个对象.

确定一个变量是哪种基本类型用typeof操作符.
确定一个变量是哪种引用类型用instanceof操作符.

作用域

1.变量是在某个特定的作用域中生命的,  作用域决定了这些变量的生命周期,以及哪些代码可以访问其中的变量.
2.JavaScript作用域只包括全局作用域和函数作用域, 并不包含块级作用域!
3.作用域是可以嵌套的, 从而形成作用链.由于作用域链的存在, 可以让变量的查找向上追溯, 既子函数可以访问父函数的作用域=>祖先函数的作用域=>直到全局作用域, 这种函数我们成为闭包.

例:

var color = "blue";
function changeColor() {
     var anotherColor = ""red;
           function swapColors() {
                   var temCorlor = anothrtColor;
                   color = temCorlor;
         //这里可以访问color, anotherColor, tempColor
       }
}
// 这里只能访问color, changeColor();

作用域的概念看着简单, 实际应用会有不少问题, 遇到问题细心分析.

内存问题:

JavaScript引擎具有自动垃圾回收机制, 不需要太关注内存分配和垃圾回收问题.

引用类型:
Object是唯一的复杂数据类型, 应用类型都是从Object类型上继承而来.

* Array : 数组类型
* Date :日期类型
* RegExp : 正则表达式类型
* 等等...

但我们用的最多的函数是Function类型:

由于Function是引用类型, 而JavaScript又可以往引用类型上加属性和方法.那么. 函数也可以!这也是javaScript函数强大和复杂的地方.也就是说: 函数也可以拥有自定义方法和属性!
JavaScript对前面提到的5种基本类型的其中三种也做了引用类型封装, 分别是Boolean, Number, String, 但使用不多, 只做了解.

注: 在所有代码执行之前, 作用域就内置了两个对象, 分别是Global和Math, 其中浏览器的Global就是window.

到此为止,JavaScript中基础的概念都差不多介绍了, 其中函数和作用域相对来说复杂一些, 其他的都很浅显.

面向对象编程:

JavaScript本身并没有类和接口的概念, 面向对象都是基于原型实现的.

定义一个类:
(我们使用构造函数+原型的方式来定义一个类)

使用构造函数创建自定义类型, 然后使用new操作符来创建类的示例, 但是构造函数上的方法和属性在每个示例上都存在, 不能共享, 于是我们引入原型来实现方法和属性的共享

例子:

//构造函数
function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = [@"Shelby", "Court"];
}
// 原型
Person.prototype = {
       constructor : Person,
       sayName: function() {
             return this.name;
       }
}
// 实例化
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.friends.push("Van");
alert(person1.friends); // 输出"Shelby, Count, Van"
alert(person2.friends);// 输出"Shelby, Count"
alert(person1.friends === person2.friends);//输出"false"
alert(person1.sayName === person2.sayName);//输出true

实现继承:
如何让子类继承父类呢?JavaScript通过原型链来实现继承!
如何构建原型链呢?将子类实例赋值给父类构造函数的原型即可.
构建原型链之后, 子类就可以访问父类的所有属性和方法!

// 父类
function SuperType() {
      this.property = true;
}
SuperType.prototype.getSuperValue = function() {
         return this.property;
}

//子类
function SubType() {
      this.subproperty = false;
}

//子类继承父类
SubType.orototype = new SuperType();
//给子类添加新方法
SubType.prototype.getSubvalue = function() {
return this.subproperty;
}
// 重写父类的方法
SubType.prototype.getSuperValue = function() {
       return false;
}
// 实例化
var instance = new SubType();
console.log(instance.getSuperValue());//输出false

函数表达式:
JavaScript中有两种定义函数的方式: 函数声明和函数表达式

使用函数表达式无需对函数命名, 从而实现动态 编程, 也既匿名函数. 有了匿名函数,JavaScript函数有了更强大的用处.

递归:

递归是一种很常见的算法, 景点例子就是斐波拉契数列. 
// 最佳实践, 函数表达式
var factorial = (function f(num)) {
       if (num <= 1) {
           return 1;
       } else {
        return num * f(num -1);    
       }
}

// 缺点1:
// factorial 存在被修改的可能
// 导致 return num *factorial (num - 1) 报错
function facorial(num) {
      if (num <= 1) {
         return 1;
      } else {
           return num * factorial(num - 1);
      }
}
//缺点2:
// argumentscallee, 规范已经不推荐使用
function factorial(num) {
      if (num <= 1) {
          return 1;
      } else {
           return num * argument.callee(num - 1);
      }
}

注: 递归就是这样,arguments.callee已经是过去式, 改回函数表达式才是最常用的,实践出真知.要注意两点1.边界条件, 通常是if-else. 2.递归调用

闭包:

如果一个函数可以访问另一个函数作用域中的变量, 那么牵着就是闭包.犹豫JavaScript函数可以返回函数, 自然, 创建闭包的常用方式就是在一个函数里创建另一个函数.
这很简单, 例如在父函数中定义子函数就可以创建闭包, 而子函数可以访问父函数的作用域.

如何解决闭包的缺陷:

0.我们通过subFuncs返回函数数组, 然后分别调用执行
1. 返回函数的数组subFuncs, 而这些函数对superFunc的变量有引用
2.当我们回头执行subFuncs中的函数的时候, 我们得到的i其实一直都是10,
3.原因是当我们返回subFuncs之后, superFunc中的i=10
4, 所以当执行subFuncs中的函数的时候, 输出i都为10.
以上就是闭包最大的坑, 一句话的理解就是: 子函数对父函数变量的引用, 是父函数运行结束之后的变量的状态
function superFunc() {
var subFuncs = new Array();
for (var i = 0; i<10; i++) {
     subFuncs[i] = function() {
          return i; 
          }
     }
      return i;
}

那么, 如何解决上诉闭包的坑呢?
其实原理很简单, 既然闭包坑的本质是:子函数对父函数变量的引用, 是父函数运行结束之后的变量的状态,那么我们解决这个问题的方式就是: 子函数对父函数变量的引用, 使用运行时的状态, 就是在函数表达式的基础上, 加上自执行就好.

function superFunc() {
       var subFuncs = new Array();
       for ( var i = 0: i <10; i++) {
             subFuncs[i] = function(num) {
                   return function() {
                               return num;
                   }
             }(i)
       }
      return subFuncs;
}

综上, 闭包本身不是什么复杂的机制,就是子函数可以访问父函数的作用域.
而由于JavaScript函数的特殊性, 我们可以返回函数, 如果我们将作为闭包的函数返回, 那么该函数引用的父函数变量是父函数运行之后的状态, 而不是运行时的状态, 这便是闭包最大的坑, 而为了解决这个坑, 我们常用的方式就是让函数表达式自执行. 此外, 由于闭包引用了祖先函数的作用域, 所以滥用闭包会有内存问题.

闭包的主要用处:封装

闭包可以封装私有变量或者封装块级作用域.
-> 封装块级作用域
JavaScript并没有块级作用域的概念, 只有全局作用域和函数作用域, 那么如果想要创建块级作用域的话, 我们可以通过闭包来模拟.
创建并立即调用一个函数, 就可以封装一个块级作用域. 该函数可以立即执行其中的代码, 内部变量执行结束就会被立即销毁.
function outputNumber(count) {
// 在函数作用域下, 利用闭包封装块级作用域
// 这样的话, i在外部不可用, 便有了类似块级的作用域
(function() {
       for (var i = 0; i < count; i++) {
            alert(i);
      }
})();
    alert(i); // 导致一个错误
}

//在全局作用域下, 利用闭包封装块级作用域
//这样的话, 代码块不会对全局作用域造成污染
(function()  {
      var now = new Date();
      if (now.getMonth() == 0 && now.getDate() == 1) {
          alert("Happy new year!");

      }
})();
// 是的, 封装块级作用域的核心就是这个: 函数表达式 + 自执行!
(function() {
     // 这里是块级作用域
})();

-->封装私有变量
JavaScript也没有私有变量的概念, 我们也可以使用闭包来实现公有方法, 通过隐藏变量暴露方法的方式来实现封装私有变量.

(function() {
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
     return false;
}
// 构造函数
MyObjc = function(){};
// 公有/特权方法
Myobject.prototype.pulicmethod = function() {
          privateVariable++;
          return privateFunction;
    };
})();

总结:

* JavaScript的基础主要包括 : 5中基本数据类型, 1种复杂数据类型, 操作符, 控制              语句,函数等.
* 了解基本的语法后, 要学习JavaScript的变量, 作用域, 作用域链
* 常见的引用类型可以边查边用, 注重正则的学习
* 面向对象编程的部分外面有很多种方式, 你只需记住使用构造函数+原型去定义一个类, 使用原型链去实现继承即可. 
* 函数表达时间引出了几个比较好玩的东西: 递归, 闭包, 封装. 记住递归的最佳实践,闭包的定义及缺陷,闭包的适用场景.

JavaScript作为一门动态语言, 和其他语言有较大的差异.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,392评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,258评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,417评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,992评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,930评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,199评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,652评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,327评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,463评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,382评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,432评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,118评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,704评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,787评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,999评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,476评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,057评论 2 341

推荐阅读更多精彩内容