资讯专栏INFORMATION COLUMN

JS基础之常用小技巧和知识总结(二)

yacheng / 751人阅读

摘要:组合使用构造函数模式和原型。构造函数用于定义实例属性,原型链用于定定方法和共享的属性。为了避免矛盾和意外的结果总是指定基数参数。

本文主要记录平时开发遇到的知识点和小技巧

原型对象与原型链

JavaScritp 引擎在访问对象的属性时,如果在对象本身中没有找到,则会去原型链中查找,如果找到,直接返回值,如果整个链都遍历且没有找到属性,则返回 undefined.原型链一般实现为一个链表,这样就可以按照一定的顺序来查找。

原型链是一个由对象组成的有限对象链,实现继承和共享属性。

var base = {
    name: "base",
    getInfo: function() {
        return this.name;
    },
    getMoreInfo: function() {
         return this.id + ":" + this.name;
    }
}
var ext1 = {
    id: 0,
    name: "ext1"
    __proto__: base
}
var ext2 = {
    id: 9,
    __proto__: base
}
var ext3 = {
    id: 10,
    __proto__: base
}
console.log(ext1.id); // 0
console.log(ext1.getInfo()); // ext1

console.log(ext2.id); // 9
console.log(ext2.getInfo()); // base

console.log(ext3.id); // 10
console.log(ext3.getMoreInfo()); // 10:base

//getMoreInfo与getInfo 函数中的 this 表示原始对象,而并非原型对象.
    
// 构造函数,所有创建的对象都会有y属性
function Foo(y) {
    this.y = y;
}
    
// 创建共享属性x
Foo.prototype.x = 10;
    
// 创建共享方法calculate
Foo.prototype.calculate = function(z) {
    return this.x + this.y + z;
}
    
// 使用foo模式创建b、c
var b = new Foo(10); // 此时b.y为10
var c = new Foo(20); // 此时c.y为20
    
//b、c分别调用共享方法
console.log(b.calculate(10)); // 30
console.log(c.calculate(10)); // 40
// 注意一点,this这个值在一个继承机制中,仍然是指向它原本属于的对象,而不是从原型链上找到它时, 它所属于的对象。
function Person() {};

Person.prototype = {
    constructor: Person,
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    friends: ["Shelby", "Court"],
    sayName: function() {
        alert(this.name);
    }
};

var person1 = new Person();

var person2 = new Person();

person1.friends.push("Grey");
console.log(person1.friends); // ["Shelby", "Court", "Grey"]
console.log(person2.friends); // ["Shelby", "Court", "Grey"]
console.log(person1.friends === person2.friends); // true

原型对象的问题,公共属性可能会被篡改。这也是为什么很少看到有人使用原型链的原因。
function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
}

Person.prototype = {
    constructor: Person,
    sayName: function() {
        alert(this.name);
    }
}

var person1 = new Person();

var person2 = new Person();

person1.friends.push("Grey");

console.log(person1.friends); // ["Shelby", "Court", "Grey"]

console.log(person2.friends); // ["Shelby", "Court"]

//组合使用构造函数模式和原型。
//构造函数用于定义实例属性,原型链用于定定方法和共享的属性。
数值转换

使用 parseInt()你可以从字符串中获取数值,该方法接受另一个基数参数,可以省略,但不推荐。

当字符串以0开头的时候,就有可能会出问题。

例如,部分时间进入表单域,在ECMAScript 3中,开头为”0′′ 的字符串被当做8进制处理了,但这已在ECMAScript 5中改变了。为了避免矛盾和意外的结果,总是指定基数参数

var month = "06", year = "09";
month = parseInt(month, 10);
year = parseInt(year, 10);

此例中,如果你忽略了基数参数,如parseInt(year), 返回的值将是 0。
因为“09”被当做8进制(好比执 行 parseInt( year, 8 )), 而09在8进制中不是个有效数字。

替换方法是将字符串转换成数字,包括:

+"08" //  8 

Number("08") // 8 显式转换
 
"08" - 0 // 8 隐式转换
Module模式

1.模块化,可重用
2.封装了变量和function, 与全局的命名空间不接触, 松耦合
3.只暴露可用public的方法,其它私有方法全部隐藏.

var Calculator = function (eq) { 
    //这里可以声明私有成员
    var eqCtl = document.getElementById(eq);
    return {
        // 暴露公开的成员
        add: function (x, y) {
            var val = x + y;
            eqCtl.innerHTML = val; 
        }
    }; 
};

// 我们可以通过如下的方式来调用:
var calculator = new Calculator("eq"); 
calculator.add(2, 2);

当然,我们也可以使用匿名函数来完成它,并且引用全局对象。

(function ($, YAHOO) {
// 这里,我们的代码就可以使用全局的jQuery对象了,YAHOO也是一样
} (jQuery, YAHOO));

Module模式的一个限制就是所有的代码都要写在一个文件,但是在一些大型项目里,将一个功能分离成多 个文件是非常重要的,因为可以多人合作易于开发。

var blogModule = (function (my) { 
    my.AddPhoto = function () {
        //添加内部代码 
    };
    return my;
} (blogModule));

上面的代码尽管可以执行,但是必须先声明blogModule,然后再执行上面的扩展代码,也就是说步骤不 能乱,怎么解决这个问题呢?我们可以在传入参数的时候进行一些改变:

var blogModule = (function (my) {
    // 添加一些功能
    return my;
} (blogModule || {}));

通过这样的代码,每个多带带分离的文件都保证这个结构,那么我们就可以实现任意顺序的加载。

自执行函数

在JavaScript里,任何function在执行的时候都会创建一个执行上下文。

因为为function声明的变量和 function有可能只在该function内部,这个上下文,在调用function的时候,提供了一种简单的方式来创 建自由变量 或 私有的子function。

当你声明一个函数的时候,通过在后面加个括弧就可以实现自执行:

// 因为想下面第一个声明的function可以在后面加一个括弧()就可以自己执行了,比如foo(),
// 因为foo仅仅是function() { /* code */ }这个表达式的一个引用

var foo = function(){ 
    /* code */ 
}
// ...是不是意味着后面加个括弧都可以自动执行?

function(){ 
/* code */ 
}(); // SyntaxError: Unexpected token

// 但是如果你在括弧()里传入一个表达式,将不会有异常抛出
// 但是foo函数依然不会执行
function foo(){ 
/* code */ 
}( 1 );

// 因为它完全等价于下面这个代码,一个function声明后面,又声明了一个毫无关系的表达式:
function foo(){ /* code */ }(1);

那么想要运行自执行函数有很多种方法:

(function () { /* code */ } ()); // 推荐使用这个

(function () { /* code */ })(); // 但是这个也是可以用的

// 由于括弧()和 JS的 &&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的
// 所以一旦解析器知道其中一个是表达式了,其它的也都默认为表达式.

// 比如:
var i = function () { return 10; }();

true && function () { /* code */ }();

0, function () { /* code */ }();

如果你不在意返回值,或者不怕难以阅读, 甚至可以在function前面加一元操作符号:

!function () { /* code */ } (); 

~function () { /* code */ } (); 

-function () { /* code */ } (); 

+function () { /* code */ } ();

// 使用new关键字,也可以用
new function () { /* code */ }
new function () { /* code */ } () // 如果需要传递参数,只需要加上括弧()
自执行匿名函数的应用

自执行匿名函数应用最多的场景可能就是闭包了。

闭包可以创建额外的scope,这可以被用来组合相关的或有依赖性的代码。用这种方式可以最大限度地减少代码干扰的危害。但是滥用闭包也会产生一系列的副作用,比如内存泄露。在实际开发中,我们应该合理的应用闭包。

看一个例子:

for (var i=1; i<=5; i++) { 
    setTimeout( function() {
        console.log(i);
    }, i * 1000 );
}

这是一个经典的闭包题,我们想要的结果其实是输出1-5,可是却发现输出的是5个6.

那是因为javascript是单线程,执行代码是以轮询的形式进行执行。

换句话说,setTimeout函数的代码,被放到了第二梯队,第一梯队是执行for循环,循环完毕之后,才会执行setTimeout里的function。

所以当for循环执行完毕之后,i的值已经是6了。

那么我们可以用匿名函数闭包来解决这个问题:

for (var i=1; i<=5; i++) { 
    (function(i) {
        setTimeout( function() {
            console.log(i);
        }, i*1000 );
    })(i)
}

在setTimeout的外层包裹一个自执行匿名函数,并传入i,根据闭包的特性,此时每次循环时的i值都保存在对应的闭包中,而不在受到外层i的影响,setTimeout里的function中输出的i值,访问的是其对应闭包中的值。

这个题目是考闭包的,其实我们使用值的传递,一样可以得到同样的结果,只不过那样的话,可能就不是考官想要的答案了。

for (var i=1; i<=5; i++) { 
    setTimeout( function(i) {
        console.log(i);
    }, i*1000 ,i);
}

我们知道,函数的参数如果是普通类型值,是按值传递的。因此可以取巧,直接传值,来得到想要的答案。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84748.html

相关文章

  • 前端资源系列(4)-前端学习资源分享&前端面试资源汇总

    摘要:特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 本以为自己收藏的站点多,可以很快搞定,没想到一入汇总深似海。还有很多不足&遗漏的地方,欢迎补充。有错误的地方,还请斧正... 托管: welcome to git,欢迎交流,感谢star 有好友反应和斧正,会及时更新,平时业务工作时也会不定期更...

    princekin 评论0 收藏0
  • 前端文档收集

    摘要:系列种优化页面加载速度的方法随笔分类中个最重要的技术点常用整理网页性能管理详解离线缓存简介系列编写高性能有趣的原生数组函数数据访问性能优化方案实现的大排序算法一怪对象常用方法函数收集数组的操作面向对象和原型继承中关键词的优雅解释浅谈系列 H5系列 10种优化页面加载速度的方法 随笔分类 - HTML5 HTML5中40个最重要的技术点 常用meta整理 网页性能管理详解 HTML5 ...

    jsbintask 评论0 收藏0
  • 前端文档收集

    摘要:系列种优化页面加载速度的方法随笔分类中个最重要的技术点常用整理网页性能管理详解离线缓存简介系列编写高性能有趣的原生数组函数数据访问性能优化方案实现的大排序算法一怪对象常用方法函数收集数组的操作面向对象和原型继承中关键词的优雅解释浅谈系列 H5系列 10种优化页面加载速度的方法 随笔分类 - HTML5 HTML5中40个最重要的技术点 常用meta整理 网页性能管理详解 HTML5 ...

    muddyway 评论0 收藏0
  • Web前端开发学习推荐--菜鸟必看

    Web前端开发是创建Web页面或app等前端界面呈现给用户的过程。第一阶段:前端基础(HTML / CSS / JavaScript / jQuery)初识HTML+CSS【学习笔记】HTML基础完结篇html基础知识——标签详解html基础知识——与用户交互!(表单标签)html基础知识——css样式①史上最全Html和CSS布局技巧面试题汇总 HTML+CSS篇CSS 最核心的几个概念纯HTM...

    JerryWangSAP 评论0 收藏0

发表评论

0条评论

yacheng

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<