美文网首页
深入理解JS

深入理解JS

作者: hutou | 来源:发表于2016-06-08 14:35 被阅读273次

说明

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

  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环境下,会有点不同

相关文章

  • 深入理解js

    延迟加载 (Lazyload) 三种实现方式 延迟加载也称为惰性加载,即在长网页中延迟加载图像。用户滚动到它们之前...

  • 深入理解JS

    说明 这篇文章将包含如下的几个内容: 对象的理解 函数的理解 封装与继承 常见的问题 一. 对象的理解 字面量通过...

  • js new 运行机制

    js手札--js中new到底做了些啥JS核心系列:理解 new 的运行机制深入理解 Javascript 运行机制及原型

  • 前端 | JS引擎的执行机制

    首先,请记住两点: JS 是单线程语言 JS 的Event Loop 是JS的执行机制.要想深入理解JS的执行,就...

  • 从源码的角度看 React JS 中批量更新 State 的策略

    在之前的文章「深入理解 React JS 中的 setState」与 「从源码的角度再看 React JS 中的 ...

  • 深入理解js Dom事件机制(二)——添加事件处理程序

    文章转载自https://segmentfault.com/a/1190000012022432 深入理解js D...

  • 深入理解JS模块

    引言 JavaScript的模块机制其实是借鉴的其他程序设计语言的, 如Java中package的概念, impo...

  • Js 深入理解Ajax

    ajax的工作原理: ajax的工作原理相当于在用户和服务器之间加了一个中间层(ajax引擎) ,使用 户操作与服...

  • 深入理解js数组

    一、如何判断是否是数组类型 1. typeof 上面的办法并不能实时的检测出是否是数组,只能判断其类型,所以说ty...

  • 深入理解js对象

    定义对象 两种方式定义对象 上面的例子创建了一个person对象实例,并为它添加了属性及方法,在早期js开发人员经...

网友评论

      本文标题:深入理解JS

      本文链接:https://www.haomeiwen.com/subject/vuihdttx.html