深入理解JS

说明

这篇文章将包含如下的几个内容:

  1. 对象的理解
  1. 函数的理解
  2. 封装与继承
  3. 常见的问题

一. 对象的理解

  1. 字面量
    通过字面量可以很容易的定义对象。在大括号中直接定义,属性:值
var o = {
    x : 1 ,
    y : 2 ,
    method : function() {
        console.info("this is a method");
    }
 }
  1. 对象的检索
    可以通过属性名直接得到属性值,也可以使用[]来获得
console.info(o.x);
console.info(o["y"]);
  1. 对象的更新
    可以直接对对象的属性进行设置操作,更新对象属性;可以直接添加属性,并赋值
o.x = 10
console.info(o.x);  // 10
o.z = 20
console.info(o.z);  //  20
  1. 对象的枚举
    可以通过for..in来进行枚举;方法也会被枚举出来
for (name in o) {
    if (o.hasOwnProperty(name)) {
        console.info("property = " + name + " and value = " + o[name]);
    }
}

这里注意:为什么有一个hasOwnProperty,是为了防止枚举出原型链上的属性。下面举例说明

Object.prototype.basex = 12
var o = {
    x : 100
}
// 这里会打印x
for (name in o) {
    if (o.hasOwnProperty(name)) {
        console.info(name);
    }
}
// 这里会打印x, basex
for (name in o) {
    console.info(name);
}

如果hasOwnProperty被改写,可以使用如下方法进行调用

var o = {
    x : 100,
    // 被改写
    hasOwnProperty : 111
}
for (name in o) {
    // 改变调用方法
    if (Object.prototype.hasOwnProperty.call(o,name)) {
        console.info(name);
    }
}
  1. 删除对象属性
delete o.x
  1. 原型链
    对象可以使用原型链上的属性
Object.prototype.x = 100
var o = {}
console.info(o.x);  //  100
delete o.x
//  不能删除原型链的属性
console.info(o.x);  //  100
//  自己定义属性
o.x = 200
//  显示自己定义的属性,覆盖原型链属性
console.info(o.x);  //  200
delete o.x
//  删除自己定义的属性,漏出原型链属性
console.info(o.x);  //  100

二. 函数的理解

函数是JS的一等公民,是JS中最灵活的部分,也是最难理解的部分

  1. 函数对象
    函数也是对象,也可以存在属性和方法
var F = function() {    
}
//  使用对象一样使用函数
F.x = 12;
console.info(F.x); // 12
  1. 函数字面量
    函数的字面量包含四个部分:保留字function;函数名(可以省略);函数参数,以逗号分隔;花括号(函数主体)。
    函数的字面量可以出现在任何允许出现表达式的地方
  2. 函数的调用
    在进行函数调用的时候,会隐含的传入this和arguments。其中arguments是一个伪数组,表示传入的参数。this可以理解为调用的上下文。函数有几种调用方式,下面将分别说明
    作为方法调用
var o = {
    x : 100,
    method : function() {
        //  方法中的调用,this表示方法的对象,这里是o
        console.info(this.x);
    }
}
o.method()  //  100

函数调用

function f(x) {
    // this指代全局变量
    this.x = x
    console.info(x);
}
try{
    console.info(x);    // not defined  
}catch(e){
    console.info(e);    // not defined error
}
// 调用函数
f(100)
console.info(x);    //  100

构造器调用,将函数作为一个构造器

//  构造器
var F = function(x) {
    this.x = x
}
//  构造器中定义方法
F.prototype.read = function() {
    return this.x
}
//  构造一个对象
var ins = new F(200)
//  调用方法(this指代对象本身)
console.info(ins.read());   //  200
console.info(ins.x);        //  200

call apply
在之前的对象说明中,我们已经展示了使用call调用方法的例子。这种调用方法,会主动改变上下文,也就是说this会由我们设定

//  定义一个对象
var o = {
    x : 100
}
//  定义一个函数
var f = function() {
    //  使用this
    console.info(this.x);
}
//  两种调用方法的区别
f()         //  undefined
f.call(o)   //  100
  1. 高阶函数
    当一个函数接受另一个函数作为参数的时候,就是高阶函数。我们可以自定义高阶函数,下面是两个使用Array中两个高阶函数的例子
    Array.map
//  定义一个数值
var arr = [1,2,3,4,5,6]
//  通过高阶函数运算
var arrD = arr.map(function(x) {
    return x*x
})
console.info(arrD); //  [ 1, 4, 9, 16, 25, 36 ]

Array.reduce

//  定义一个数值
var arr = [1,2,3,4,5,6]
//  通过高阶函数进行求和运算
var d = arr.reduce(function(x,y) {
    return x+y
})
console.info(d);    //  21
  1. 闭包
    内层函数可以使用外层函数的特点叫做闭包。下面会讲述使用闭包解决一些常见的错误

三. 封装与继承

javascript是面向对象的,但是没有语法层面的继承和封装语法。

  1. 定义类
    js中我们可以有类似java的使用方法:定义对象,通过new实例化一个对象
 // 定义一个类
 var F = function() {}
 // 定义类中的方法
 F.prototype.test = function(o) {
    console.info(o);
 }
 F.prototype.display = function() {
    console.info("this is a display function");
 }
 // 实例化一个类
 var ins = new F;
 ins.test('lxm')
 ins.display()
  1. 封装
    通过字面量定义的对象是无法保护属性的,可以随意的修改和增减。下面介绍一个数据封装的方法
//  创建构造器
var F = function() {
    //  私有属性
    var x;
    //  this是必须的
    this.getX = function() {
        return x;
    }
    this.setX = function(x1) {
        x = x1;
    }
}
//  通过构造器创建对象
var instance = new F
//  使用对象
instance.setX(100)
console.info(instance.x);       //  undefined
console.info(instance.getX());  //  100
  1. 继承
//  创建一个继承函数
if(typeof Object.inhert !== 'function'){
    Object.inhert = function(o) {
        var F = function() {}
        F.prototype = o
        return new F
    }
}
//  父对象
var o = {
    x : 1,
    y : 100
}
//  创建子对象
var child = Object.inhert(o)
console.info(child.x);  //  1
console.info(o.x);      //  1
//  设置x
child.x = 200
console.info(child.x);  //  200
console.info(o.x);      //  1

四. 常见的问题

整理一下在实际工作中经常发生的误用

  1. 全局作用域
      myglobal = "hello"; // 不推荐写法
      console.log(myglobal); // "hello"
      console.log(window.myglobal); // "hello"
      console.log(window["myglobal"]); // "hello"
      console.log(this.myglobal); // "hello"  

使用显示声明的变量不能被delete删除

var sss = 'abc';
ddd = 'abc';
delete sss; // false
delete ddd; // true
  1. 函数内使用var避免产生全局变量
    function abc(a,b){
      ret = a + b;
      return ret;
    }  

    console.info(abc(1,2));
    console.info(this.ret);
  1. 定义变量产生的全局变量
    function abc(a,b){
      var ret = a + b;
      var x = y = 3;
      return ret;
    }  

    console.info(abc(1,2));
    console.info(this.ret);        
    console.info(y);
  1. 变量提升
    function abc(){
      alert(myname);
      var mynam ='lxm';
      alert(myname);
    }
第一阶段是变量,函数声明,以及正常格式的参数创建,这是一个解析和进入上下文 的阶段。
第二个阶段是代码执行,函数表达式和不合格的标识符(为声明的变量)被创建
  1. 自动添加分行
    function abc(){
      var s = 12;
      return
      s
    }

    abc();        
  1. for循环中产生全局变量
    for(var i=0;i < 12;i++){
    }

    console.info("gloabl i = " + i);

注意缓存数组的长度,尤其是进行dom操作的时候

    for(var i=0, max=document.getElementsByName().length;i<max;i++){
    }
  1. 循环中常见的错误
    var funArr = [];
    for(var i=0;i<10;i++){
      funArr[i] = function(){
        console.info(i);
      }
    }
    funArr[2]();

通过闭包来解决

    var funArr = [];
    for(var i=0;i<10;i++){
      (function(i){
        funArr[i] = function(){
          console.info(i);
        }
      })(i);
    }
    funArr[2]();
  1. 使用for..in进行属性遍历
    进行枚举,但是不保证顺序;通常需要过滤原型链上的属性
    // 对象
    var man = {
       hands: 2,
       legs: 2,
       heads: 1
    };
    for(var t in man){
      if(man.hasOwnProperty(t))
        console.info(t);
    }        

另外的一种调用方式,可以避免对象将hasOwnProperty重新定义

    for (var i in man) {
       if (Object.prototype.hasOwnProperty.call(man, i)) { // 过滤
          console.log(i, ":", man[i]);
       }
    }
  1. 关于扩展内置属性的问题
    由于有了prototype的存在,可以很方便的进行属性的扩充
    最好不增加内置原型
    Object.prototype.testMethod = function(){
      //console.info('这是我弄的!');
      return "这是我弄的";
    }

    var sss = {};
    sss.testMethod();
  1. 尽可能避免隐式类型转换
    使用 === !== 来进行判断
  2. eval尽可能不要用
    这个处理可以接收任何字符串,当成js代码来执行
  3. 关于类型转换
    parseInt尽可能少用,使用Number()来替换
  4. 内嵌方法中的this错误
    通常我们在Java等面向对象的语音中都见过this,指代当前的对象,在编译期间就已经确定下来,是编译期绑定的。而javascript是解释性语言,this是动态的,是运行期绑定的,这就导致了this关键字具有多重含义,不好理解。下面是一个js设计中的问题
var x = 'global'
var o = {
    x : 'property',
    dis : function() {
        var x = 'local'
        console.info("dis x = " + this.x);          //  property
        //  定义一个内嵌方法
        var local = function() {
            console.info("local x = " + this.x);    //  global
        }
        //  执行内嵌方法
        local()
    }
}
o.dis()

这个测试结果是在浏览器中执行,如果在Node.js环境下,会有点不同

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

推荐阅读更多精彩内容

  • 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。 一、变量...
    zock阅读 1,075评论 2 6
  • 单例模式 适用场景:可能会在场景中使用到对象,但只有一个实例,加载时并不主动创建,需要时才创建 最常见的单例模式,...
    Obeing阅读 2,065评论 1 10
  • 《ijs》速成开发手册3.0 官方用户交流:iApp开发交流(1) 239547050iApp开发交流(2) 10...
    叶染柒丶阅读 5,118评论 0 7
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,750评论 2 17
  • 这一节讲真值函数论证。 介绍3种方法检验真值函数论证,真值表法、简化真值表法和演绎法。 先说第一种,我们已经知道真...
    JYQC66阅读 1,225评论 0 0